From 443e6027f8861b958a82d5f63852dc6116bbc07d Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Tue, 30 Apr 2024 23:39:51 +0200 Subject: [PATCH 01/46] feat: Client provider spec Signed-off-by: Thomas Poignant --- provider/specs/client.md | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 provider/specs/client.md diff --git a/provider/specs/client.md b/provider/specs/client.md new file mode 100644 index 0000000..85e5cb1 --- /dev/null +++ b/provider/specs/client.md @@ -0,0 +1,82 @@ +# Creating an OFREP client provider + +OpenFeature Remote Evaluation Protocol (OFREP) is an API specification for feature flagging that allows the use of generic providers to connect to any feature flag management systems that supports the protocol. + +In this specification we will specify how to write a OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. +We will keep the specification language agnostic. + +**Pre-requisite:** +- Understanding of [general provider concepts](https://openfeature.dev/docs/reference/concepts/provider/) +- Understanding of the [OFREP](../../README.md) +- Understanding of the [OFREP OpenAPI specification](../../service/openapi.yaml) + + +## Constructor +An implementation of a OFREP client provider should allow in the creation of the provider to take as options: +- `baseURL`: The base URL of the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system). This should be the base of the URL pointing before the `/ofrep` namespace of the API. +- `headers`: The headers to use when calling the OFREP endpoints *(ex:`Authorization`, Custom headers, etc ...)*. +- `pollInteral`: The polling interval defining when to call again the flag management system. + +In the constructor the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. + +## Initialize the provider +An implementation of a OFREP client provider should start with an initialization of the provider. + +The `initialize()` function should follow those steps: +1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* +2. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. + - If the endpoint return an error, the `initialize()` function must error and exit. + - If the endpoint is in success, we should store in a local cache all the flags evaluation result returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. +3. If polling is enabled, the function should start the polling loop *(See [polling section](#polling))*. + +## Evaluation +The evaluation should not do any remote API calls. + +When calling an evaluation function the provider should check if the associated type is supported, by checking the key `capabilities.flagEvaluation.unsupportedTypes` from the configuration endpoint. +- If the type is unsupported, we should exit early and directly return an error. +- If the type is supported, we should retrieve the flag from the local cache of the flags evaluation. + - If you are not able to find the flag, you should return an FlagNotFound error. + - If the remote evaluation for this flag has return an error, you should map the error in provider and return the associated error. + - If the value retrieve from the cache has a different type than the one expected you should return a TypeMismatch error. + - If the cached evaluation is in success you should return the evaluation response. + +## Polling +The polling system will make a POST request periodically tp the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. + +If we have a `ETag` available we should always add the header `If-None-Match` with the `ETag` value. +- If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. +- If the cache is outdated we will receive a `200` with the new values of all the flags. In that situation we should: + 1. Replace the actual local cache of flags evaluations. + 2. Store the new `ETag` value for the future call. + + +## Annexe 1 +**`/ofrep/v1/configuration` description:** +The endpoint will return a list of configuration for the provider: +- `name`: Name of the flag management system, it should be used in the `metadata.name` name (ex: `OFREP Web Provider ${metadata.name}`). +- `capabilities`: List of capabilities of the flag management system and their associated configuration. *Check the [capabilities section](#capabilities) for the details of each capability.* + +### Capabilities +In this section we will describe each capabilities and describe their default value and usage. + +#### Cache Invalidation +The capability `cacheInvalidation` describe how the mechanism of cache invalidation is working. + +##### Polling +`polling`: define how the provider should do the polling + +- `enabled`: if `true` the provider should poll the `/ofrep/v1/evaluate/flags` regularly to check if any flag evaluation has changed in the flag management system. +- `minPollingInterval`: define the minimum polling interval acceptable by the flag management system. If for any reason the `pollInteral` provided in the constructor is lower than this `minPollingInterval` we should default on this value. + +If the key `polling` is not available, the provider should use those default values: +```json +{ + "enabled": true, + "minPollingInterval": 0 +} +``` + +#### Flag Evaluation +`flagEvaluation`: define the how to managed flag evaluation in the provider. +- `unsupportedTypes`: Some flag management system are not supporting all types. This array should contains all the types not supported by the flag management system.Acceptable values are `int`, `float`,`string`, `boolean`,`object`. + Default value: `[]` \ No newline at end of file From 316011917785afff8d86789d0820fcb21e137eda Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:53:13 +0200 Subject: [PATCH 02/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 85e5cb1..9f4f770 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -2,7 +2,7 @@ OpenFeature Remote Evaluation Protocol (OFREP) is an API specification for feature flagging that allows the use of generic providers to connect to any feature flag management systems that supports the protocol. -In this specification we will specify how to write a OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. +In this specification we will specify how to write an OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. We will keep the specification language agnostic. **Pre-requisite:** From a68cc5fa196768d6f3b12fbef0f5cd1fd5764a30 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:53:20 +0200 Subject: [PATCH 03/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 9f4f770..f76ae6a 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -12,7 +12,7 @@ We will keep the specification language agnostic. ## Constructor -An implementation of a OFREP client provider should allow in the creation of the provider to take as options: +An implementation of an OFREP client provider should allow in the creation of the provider to take as options: - `baseURL`: The base URL of the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system). This should be the base of the URL pointing before the `/ofrep` namespace of the API. - `headers`: The headers to use when calling the OFREP endpoints *(ex:`Authorization`, Custom headers, etc ...)*. - `pollInteral`: The polling interval defining when to call again the flag management system. From 82c5a0d0f72235d2b3c9eb8798ba61524747ac2e Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:53:29 +0200 Subject: [PATCH 04/46] Update provider/specs/client.md Co-authored-by: Michael Beemer Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index f76ae6a..1cd12ca 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -17,7 +17,7 @@ An implementation of an OFREP client provider should allow in the creation of th - `headers`: The headers to use when calling the OFREP endpoints *(ex:`Authorization`, Custom headers, etc ...)*. - `pollInteral`: The polling interval defining when to call again the flag management system. -In the constructor the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. +In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. ## Initialize the provider An implementation of a OFREP client provider should start with an initialization of the provider. From c5737554ffa1f490a87dbd71d3a8c9bf14471e17 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:53:37 +0200 Subject: [PATCH 05/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 1cd12ca..1d6417a 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -20,7 +20,7 @@ An implementation of an OFREP client provider should allow in the creation of th In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. ## Initialize the provider -An implementation of a OFREP client provider should start with an initialization of the provider. +An implementation of an OFREP client provider should start with an initialization of the provider. The `initialize()` function should follow those steps: 1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* From bebb9aa51d4b317fb65db3150e422e6e59aaba86 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:53:42 +0200 Subject: [PATCH 06/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 1d6417a..f0c431d 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -25,7 +25,7 @@ An implementation of an OFREP client provider should start with an initializatio The `initialize()` function should follow those steps: 1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* 2. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. - - If the endpoint return an error, the `initialize()` function must error and exit. + - If the endpoint returns an error, the `initialize()` function must error and exit. - If the endpoint is in success, we should store in a local cache all the flags evaluation result returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. 3. If polling is enabled, the function should start the polling loop *(See [polling section](#polling))*. From 9cad8cea8611f145c177b9e7016663dd340d3dc7 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:53:54 +0200 Subject: [PATCH 07/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index f0c431d..c0c6dfd 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -26,7 +26,7 @@ The `initialize()` function should follow those steps: 1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* 2. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. - If the endpoint returns an error, the `initialize()` function must error and exit. - - If the endpoint is in success, we should store in a local cache all the flags evaluation result returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. + - If the request is successful, we should store in a local cache all of the flags evaluation results returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. 3. If polling is enabled, the function should start the polling loop *(See [polling section](#polling))*. ## Evaluation From d6ca63a83054bc9b34136e794fb06437be1bdd97 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 22:54:01 +0200 Subject: [PATCH 08/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index c0c6dfd..e9c1b65 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -30,7 +30,7 @@ The `initialize()` function should follow those steps: 3. If polling is enabled, the function should start the polling loop *(See [polling section](#polling))*. ## Evaluation -The evaluation should not do any remote API calls. +The evaluation should not perform any remote API calls. When calling an evaluation function the provider should check if the associated type is supported, by checking the key `capabilities.flagEvaluation.unsupportedTypes` from the configuration endpoint. - If the type is unsupported, we should exit early and directly return an error. From 78345e13e7bfe0030e1ddf8210f5cb4e9bcc0b55 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:09:35 +0200 Subject: [PATCH 09/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index e9c1b65..5df7175 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -52,7 +52,7 @@ If we have a `ETag` available we should always add the header `If-None-Match` wi ## Annexe 1 **`/ofrep/v1/configuration` description:** -The endpoint will return a list of configuration for the provider: +The endpoint will return a list of configurations for the provider: - `name`: Name of the flag management system, it should be used in the `metadata.name` name (ex: `OFREP Web Provider ${metadata.name}`). - `capabilities`: List of capabilities of the flag management system and their associated configuration. *Check the [capabilities section](#capabilities) for the details of each capability.* From 2944b018e26f32774cbf8b32dbacc1c81d4e745f Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:09:42 +0200 Subject: [PATCH 10/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 5df7175..8794be2 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -57,7 +57,7 @@ The endpoint will return a list of configurations for the provider: - `capabilities`: List of capabilities of the flag management system and their associated configuration. *Check the [capabilities section](#capabilities) for the details of each capability.* ### Capabilities -In this section we will describe each capabilities and describe their default value and usage. +In this section we will describe each capability and describe their default value and usage. #### Cache Invalidation The capability `cacheInvalidation` describe how the mechanism of cache invalidation is working. From faf79e8496bfdc75f46cf8ea8974d414f80c2660 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:09:50 +0200 Subject: [PATCH 11/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 8794be2..8533d2a 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -60,7 +60,7 @@ The endpoint will return a list of configurations for the provider: In this section we will describe each capability and describe their default value and usage. #### Cache Invalidation -The capability `cacheInvalidation` describe how the mechanism of cache invalidation is working. +The capability `cacheInvalidation` describes how the mechanism of cache invalidation works. ##### Polling `polling`: define how the provider should do the polling From 4c93a4b04dc68e9499cfe0964432483cb832ddd6 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:09:56 +0200 Subject: [PATCH 12/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 8533d2a..727ec75 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -77,6 +77,6 @@ If the key `polling` is not available, the provider should use those default val ``` #### Flag Evaluation -`flagEvaluation`: define the how to managed flag evaluation in the provider. +`flagEvaluation`: define how to manage flag evaluation in the provider. - `unsupportedTypes`: Some flag management system are not supporting all types. This array should contains all the types not supported by the flag management system.Acceptable values are `int`, `float`,`string`, `boolean`,`object`. Default value: `[]` \ No newline at end of file From df24b14b3674f79a3c16790b613bb105cbe4719f Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:10:14 +0200 Subject: [PATCH 13/46] Update provider/specs/client.md Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 727ec75..3a9b0d4 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -78,5 +78,5 @@ If the key `polling` is not available, the provider should use those default val #### Flag Evaluation `flagEvaluation`: define how to manage flag evaluation in the provider. -- `unsupportedTypes`: Some flag management system are not supporting all types. This array should contains all the types not supported by the flag management system.Acceptable values are `int`, `float`,`string`, `boolean`,`object`. +- `unsupportedTypes`: Some flag management systems do not support all types. This array should contain all the types not supported by the flag management system. Acceptable values are `int`, `float`, `string`, `boolean`, and `object`. Default value: `[]` \ No newline at end of file From fdb1d1dc12965c32bd3faaabf51ca9e72377b1df Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:13:32 +0200 Subject: [PATCH 14/46] Update provider/specs/client.md Co-authored-by: Michael Beemer Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index 3a9b0d4..e832dad 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -43,7 +43,7 @@ When calling an evaluation function the provider should check if the associated ## Polling The polling system will make a POST request periodically tp the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. -If we have a `ETag` available we should always add the header `If-None-Match` with the `ETag` value. +If an `ETag` is available we should always add the header `If-None-Match` with the `ETag` value. - If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. - If the cache is outdated we will receive a `200` with the new values of all the flags. In that situation we should: 1. Replace the actual local cache of flags evaluations. From 6c4a79ff7c877008daf7fbd452b3672bf55aa95d Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:14:12 +0200 Subject: [PATCH 15/46] Update provider/specs/client.md Co-authored-by: Michael Beemer Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- provider/specs/client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index e832dad..bd78194 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -41,7 +41,7 @@ When calling an evaluation function the provider should check if the associated - If the cached evaluation is in success you should return the evaluation response. ## Polling -The polling system will make a POST request periodically tp the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. +The polling system will make a POST request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. If an `ETag` is available we should always add the header `If-None-Match` with the `ETag` value. - If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. From 40964d5159cecbff7687c404be64c05b76e8f727 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:18:18 +0200 Subject: [PATCH 16/46] update with review comments Signed-off-by: Thomas Poignant --- provider/specs/client.md | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/provider/specs/client.md b/provider/specs/client.md index bd78194..388bb73 100644 --- a/provider/specs/client.md +++ b/provider/specs/client.md @@ -2,7 +2,7 @@ OpenFeature Remote Evaluation Protocol (OFREP) is an API specification for feature flagging that allows the use of generic providers to connect to any feature flag management systems that supports the protocol. -In this specification we will specify how to write an OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. +In this document, we will specify how to write an OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. We will keep the specification language agnostic. **Pre-requisite:** @@ -40,8 +40,21 @@ When calling an evaluation function the provider should check if the associated - If the value retrieve from the cache has a different type than the one expected you should return a TypeMismatch error. - If the cached evaluation is in success you should return the evaluation response. +```mermaid +flowchart TD + A[evaluation\nfunction] --> B{Is function type in\nunsupportedTypes?} + B --> |YES| C(return an error) + B --> |NO| D{Is flag key stored\nin local cache?} + D --> |NO| E(return a FlagNotFound error) + D --> |YES| F{Is cached evaluation\nresponse in error?} + F --> |YES| G(Map the error as a\nprovider error and return) + F --> |NO| H{Is the flag value the\nsame type as the\nevaluation function?} + H --> |YES| I(return the evaluation response) + H --> |NO| J(return a TypeMismatch error) +``` + ## Polling -The polling system will make a POST request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. +The polling system will make a POST request periodically tp the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. If an `ETag` is available we should always add the header `If-None-Match` with the `ETag` value. - If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. @@ -69,12 +82,12 @@ The capability `cacheInvalidation` describes how the mechanism of cache invalida - `minPollingInterval`: define the minimum polling interval acceptable by the flag management system. If for any reason the `pollInteral` provided in the constructor is lower than this `minPollingInterval` we should default on this value. If the key `polling` is not available, the provider should use those default values: -```json -{ - "enabled": true, - "minPollingInterval": 0 -} -``` + +| key | default value | +| -------------------- | ------------- | +| `enabled` | `true` | +| `minPollingInterval` | 60000 | + #### Flag Evaluation `flagEvaluation`: define how to manage flag evaluation in the provider. From 306dec580ca8f18ff095a07397c894f532b062be Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:20:34 +0200 Subject: [PATCH 17/46] Move to guideline folder Signed-off-by: Thomas Poignant --- provider/specs/client.md => guideline/static-context-provider.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename provider/specs/client.md => guideline/static-context-provider.md (100%) diff --git a/provider/specs/client.md b/guideline/static-context-provider.md similarity index 100% rename from provider/specs/client.md rename to guideline/static-context-provider.md From 7560a8b60dc3f5261fd00e012ce41790191cf980 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 2 May 2024 23:22:20 +0200 Subject: [PATCH 18/46] Replace specification Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 388bb73..0980ac1 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -3,7 +3,7 @@ OpenFeature Remote Evaluation Protocol (OFREP) is an API specification for feature flagging that allows the use of generic providers to connect to any feature flag management systems that supports the protocol. In this document, we will specify how to write an OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. -We will keep the specification language agnostic. +We will keep the document language agnostic. **Pre-requisite:** - Understanding of [general provider concepts](https://openfeature.dev/docs/reference/concepts/provider/) From 9bd563078eb84b61b0e9775fa19e07db9a415ada Mon Sep 17 00:00:00 2001 From: Kavindu Dodanduwa Date: Mon, 13 May 2024 06:18:38 -0700 Subject: [PATCH 19/46] add OpenAPI spec validator based on redocly cli (#15) Signed-off-by: Kavindu Dodanduwa Signed-off-by: Thomas Poignant --- .github/workflows/spec-validate.yaml | 20 ++++++++++++++++++++ service/openapi.yaml | 4 ++++ 2 files changed, 24 insertions(+) create mode 100644 .github/workflows/spec-validate.yaml diff --git a/.github/workflows/spec-validate.yaml b/.github/workflows/spec-validate.yaml new file mode 100644 index 0000000..9bb47e9 --- /dev/null +++ b/.github/workflows/spec-validate.yaml @@ -0,0 +1,20 @@ +name: OpenAPI spec validation action + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Run OpenAPI spec validation + run: | + docker run --rm -v $PWD:/spec redocly/cli lint ./service/openapi.yaml + echo "{exit_code}={$?}" >> $GITHUB_STATE \ No newline at end of file diff --git a/service/openapi.yaml b/service/openapi.yaml index 8783fce..55f34ef 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -1,4 +1,6 @@ openapi: 3.1.0 +servers: + - url: / info: version: 0.1.0 title: OpenFeature Remote Evaluation Protocol (OFREP) @@ -49,6 +51,7 @@ paths: $ref: '#/components/schemas/generalErrorResponse' /ofrep/v1/evaluate/flags/{key}: post: + summary: OFREP single flag evaluation contract description: OFREP single flag evaluation request parameters: - name: key @@ -102,6 +105,7 @@ paths: $ref: '#/components/schemas/generalErrorResponse' /ofrep/v1/evaluate/flags: post: + summary: OFREP bulk flag evaluation contract description: OFREP bulk evaluation request parameters: - in: header From b1035d5080571976d5f5dbfbf76336b3b8b0335b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 09:49:53 +0200 Subject: [PATCH 20/46] chore(deps): update actions/checkout action to v4 (#16) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Signed-off-by: Thomas Poignant --- .github/workflows/spec-validate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spec-validate.yaml b/.github/workflows/spec-validate.yaml index 9bb47e9..2a990b3 100644 --- a/.github/workflows/spec-validate.yaml +++ b/.github/workflows/spec-validate.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Run OpenAPI spec validation run: | From f7ae6f6b4614b40067a9e7ebd356ad975d30f36d Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 17 May 2024 18:56:00 +0200 Subject: [PATCH 21/46] Update guideline/static-context-provider.md Co-authored-by: Michael Beemer Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 0980ac1..23e9c62 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -54,7 +54,7 @@ flowchart TD ``` ## Polling -The polling system will make a POST request periodically tp the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. +The polling system will make a POST request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. If an `ETag` is available we should always add the header `If-None-Match` with the `ETag` value. - If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. From 2ee1334cc8c27288c1d291df1c70b3c34f9d0ff8 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 17 May 2024 19:00:18 +0200 Subject: [PATCH 22/46] Update guideline/static-context-provider.md Co-authored-by: Lukas Reining Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 23e9c62..477a2de 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -27,7 +27,7 @@ The `initialize()` function should follow those steps: 2. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. - If the endpoint returns an error, the `initialize()` function must error and exit. - If the request is successful, we should store in a local cache all of the flags evaluation results returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. -3. If polling is enabled, the function should start the polling loop *(See [polling section](#polling))*. +3. If polling is enabled, the polling loop should start now *(See [polling section](#polling))*. ## Evaluation The evaluation should not perform any remote API calls. From 93ae648797faf52c247f04eabe9223fa1082b7ed Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 17 May 2024 19:08:39 +0200 Subject: [PATCH 23/46] Update guideline/static-context-provider.md Co-authored-by: Lukas Reining Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 477a2de..18e1cb3 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -56,7 +56,7 @@ flowchart TD ## Polling The polling system will make a POST request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. -If an `ETag` is available we should always add the header `If-None-Match` with the `ETag` value. +If an `ETag` of a former evaluation is available we should always add the header `If-None-Match` with the `ETag` value. - If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. - If the cache is outdated we will receive a `200` with the new values of all the flags. In that situation we should: 1. Replace the actual local cache of flags evaluations. From 9c0d58e8ebfc8625e1650f13e10f3d5a4ff444e1 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 8 May 2025 12:11:18 +0200 Subject: [PATCH 24/46] update guidelines after removing configuration endpoint Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 117 ++++++++++++--------------- 1 file changed, 52 insertions(+), 65 deletions(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 18e1cb3..c3d2cd5 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -10,86 +10,73 @@ We will keep the document language agnostic. - Understanding of the [OFREP](../../README.md) - Understanding of the [OFREP OpenAPI specification](../../service/openapi.yaml) - ## Constructor An implementation of an OFREP client provider should allow in the creation of the provider to take as options: -- `baseURL`: The base URL of the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system). This should be the base of the URL pointing before the `/ofrep` namespace of the API. +- `baseURL`: The base URL of the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system). + This should be the base of the URL pointing before the `/ofrep` namespace of the API. + - In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. - `headers`: The headers to use when calling the OFREP endpoints *(ex:`Authorization`, Custom headers, etc ...)*. -- `pollInteral`: The polling interval defining when to call again the flag management system. - -In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. +- `pollInterval`: The polling interval defining when to call again the flag management system. + - If `pollInterval` is equals to 0, polling will be disabled. ## Initialize the provider An implementation of an OFREP client provider should start with an initialization of the provider. The `initialize()` function should follow those steps: -1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* -2. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. - - If the endpoint returns an error, the `initialize()` function must error and exit. - - If the request is successful, we should store in a local cache all of the flags evaluation results returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. -3. If polling is enabled, the polling loop should start now *(See [polling section](#polling))*. +1. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. + + **Request body example**: + ```json + { + "context": { + "targetingKey": "f021d0f9-33b7-4b22-b0bd-9fec66ba1d7d", + "firstname": "foo", + "lastname": "bar", + "email": "foo.bar@ofrep.dev" + } + } + ``` + + - If the endpoint returns an error, the `initialize()` function must error and exit. + - If the request is successful: + - We should store in a local cache all of the flags evaluation details returned by the API. + - We should store the `ETag` header in the provider to be able to send it back later. +2. If `pollInterval` is more than `0`, we should start a polling mechanism to periodically check for flag changes *([See polling section](#polling))*. ## Evaluation -The evaluation should not perform any remote API calls. +The evaluation should not perform any remote API calls and use the local cache. + +When calling an evaluation function, the provider should follow those steps: +1. Retrieve the evaluation details for this flag from the local cache. +2. If you are not able to find the flag in the local cache, we should return an `FLAG_NOT_FOUND` error. +3. If the evaluation details contains an error, we should map the error in provider to an OpenFeature error and return it. +4. If the value of the evaluation details in the cache has a different type than the one expected by the evaluation function you should return a `TYPE_MISMATCH` error. +5. If the evaluation details retrieved from the cache is successful, you should return the evaluation details. -When calling an evaluation function the provider should check if the associated type is supported, by checking the key `capabilities.flagEvaluation.unsupportedTypes` from the configuration endpoint. -- If the type is unsupported, we should exit early and directly return an error. -- If the type is supported, we should retrieve the flag from the local cache of the flags evaluation. - - If you are not able to find the flag, you should return an FlagNotFound error. - - If the remote evaluation for this flag has return an error, you should map the error in provider and return the associated error. - - If the value retrieve from the cache has a different type than the one expected you should return a TypeMismatch error. - - If the cached evaluation is in success you should return the evaluation response. ```mermaid flowchart TD - A[evaluation\nfunction] --> B{Is function type in\nunsupportedTypes?} - B --> |YES| C(return an error) - B --> |NO| D{Is flag key stored\nin local cache?} - D --> |NO| E(return a FlagNotFound error) - D --> |YES| F{Is cached evaluation\nresponse in error?} - F --> |YES| G(Map the error as a\nprovider error and return) - F --> |NO| H{Is the flag value the\nsame type as the\nevaluation function?} + A[evaluation function] --> D{Do we have an entry in cache for this flag key ?} + D --> |NO| E(return a FLAG_NOT_FOUND error) + D --> |YES| F{Is cached evaluation details in error?} + F --> |YES| G(Map the error as an OpenFeature provider error and return it) + F --> |NO| H{Are the flag and evaluation return types the same?} H --> |YES| I(return the evaluation response) - H --> |NO| J(return a TypeMismatch error) + H --> |NO| J(return a TYPE_MISMATCH error) ``` ## Polling -The polling system will make a POST request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. - -If an `ETag` of a former evaluation is available we should always add the header `If-None-Match` with the `ETag` value. -- If the cache is still up-to-date we will receive a `304` telling us that the nothing has changed on the flag management system side. -- If the cache is outdated we will receive a `200` with the new values of all the flags. In that situation we should: - 1. Replace the actual local cache of flags evaluations. - 2. Store the new `ETag` value for the future call. - - -## Annexe 1 -**`/ofrep/v1/configuration` description:** -The endpoint will return a list of configurations for the provider: -- `name`: Name of the flag management system, it should be used in the `metadata.name` name (ex: `OFREP Web Provider ${metadata.name}`). -- `capabilities`: List of capabilities of the flag management system and their associated configuration. *Check the [capabilities section](#capabilities) for the details of each capability.* - -### Capabilities -In this section we will describe each capability and describe their default value and usage. - -#### Cache Invalidation -The capability `cacheInvalidation` describes how the mechanism of cache invalidation works. - -##### Polling -`polling`: define how the provider should do the polling - -- `enabled`: if `true` the provider should poll the `/ofrep/v1/evaluate/flags` regularly to check if any flag evaluation has changed in the flag management system. -- `minPollingInterval`: define the minimum polling interval acceptable by the flag management system. If for any reason the `pollInteral` provided in the constructor is lower than this `minPollingInterval` we should default on this value. - -If the key `polling` is not available, the provider should use those default values: - -| key | default value | -| -------------------- | ------------- | -| `enabled` | `true` | -| `minPollingInterval` | 60000 | - - -#### Flag Evaluation -`flagEvaluation`: define how to manage flag evaluation in the provider. -- `unsupportedTypes`: Some flag management systems do not support all types. This array should contain all the types not supported by the flag management system. Acceptable values are `int`, `float`, `string`, `boolean`, and `object`. - Default value: `[]` \ No newline at end of file +The polling system will make a `POST` request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. +The goal is to be able to know when a flag has changed for this evaluation context. + +When you call the API if an `ETag` of a previous evaluation is available it is required to add the header `If-None-Match` with the `ETag` value to the `POST` request. + +When calling the API you can have those response cocde: +- `304`: Means that your current cache is up-to-date. +- `401`, `403`: The provider is not authorized to call the OFREP API. In that situation we should return an error and stop polling. +- `429`: You have reached the rate limit of the flag management system. In that situation the provider should read the `Retry-After` header from the response and ensure that we are not calling the endpoint again before this date. +- `200`: The cache is outdated. In that situation we should: + 1. Clear the actual cache. + 2. Cache the evaluation details received. + 3. Read the `ETag` header and store it for future call the API. + 4. Emit an `ConfigurationChanged` event containing in the `flagsChanged` field, a list of the flag key that have changed. From 74ea70935ef9f8968ebc73fda69f3cf9bcf63758 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 8 May 2025 13:25:18 +0200 Subject: [PATCH 25/46] adding change context Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index c3d2cd5..0f6db48 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -66,12 +66,12 @@ flowchart TD ``` ## Polling -The polling system will make a `POST` request periodically to the `/ofrep/v1/evaluate/flags` endpoint to check if there is a change in the flags evaluation to be able to store it. +The polling system will make a `POST` request periodically to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if there is a change in the flags evaluation to be able to store it. The goal is to be able to know when a flag has changed for this evaluation context. When you call the API if an `ETag` of a previous evaluation is available it is required to add the header `If-None-Match` with the `ETag` value to the `POST` request. -When calling the API you can have those response cocde: +When calling the API you can have those response codes: - `304`: Means that your current cache is up-to-date. - `401`, `403`: The provider is not authorized to call the OFREP API. In that situation we should return an error and stop polling. - `429`: You have reached the rate limit of the flag management system. In that situation the provider should read the `Retry-After` header from the response and ensure that we are not calling the endpoint again before this date. @@ -80,3 +80,8 @@ When calling the API you can have those response cocde: 2. Cache the evaluation details received. 3. Read the `ETag` header and store it for future call the API. 4. Emit an `ConfigurationChanged` event containing in the `flagsChanged` field, a list of the flag key that have changed. + +## Change context +In the client providers, a change of context should be handle to retrieve a new version of the cache. + +When the function `onContextChange` is called, we should call the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if we have to update the cache. In that situation we should handle the response the same way we are doing it for polling. \ No newline at end of file From b8a9ade78aea43a41145e6e33c550c557d67da7e Mon Sep 17 00:00:00 2001 From: Michel TURPIN Date: Thu, 30 May 2024 07:28:46 +0200 Subject: [PATCH 26/46] feat: Allow any reason (#20) Signed-off-by: Thomas Poignant --- service/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 55f34ef..0c34714 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -210,7 +210,7 @@ components: $ref: "#/components/schemas/key" reason: type: string - enum: [ STATIC,TARGETING_MATCH,SPLIT,DISABLED,UNKNOWN ] + examples: [ STATIC,TARGETING_MATCH,SPLIT,DISABLED,UNKNOWN ] description: An OpenFeature reason for the evaluation variant: type: string From 581661af675104023d199c28a9f52ea54c1595cd Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Mon, 10 Jun 2024 09:20:54 +0200 Subject: [PATCH 27/46] feat!: change minPollingInterval field name to mention millisecond (#25) * feat!: change minPollingInterval field name to mention millisecond Signed-off-by: Thomas Poignant * change name to ms Signed-off-by: Thomas Poignant --------- Signed-off-by: Thomas Poignant --- service/openapi.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 0c34714..2fa8cb5 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -360,9 +360,11 @@ components: enabled: type: boolean description: set to true if the remote flag management system is supporting polling - minPollingInterval: + minPollingIntervalMs: type: number - description: minimum polling interval (in millisecond) supported by the flag management system. The provider should ensure not to set any polling value under this minimum. + description: | + Minimum polling interval (in millisecond) supported by the flag management system. + The provider should ensure not to set any polling value under this minimum. examples: - 60000 required: From cf58be936e1da4719d4d8876c91fa64cb4588095 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Mon, 10 Jun 2024 09:27:48 +0200 Subject: [PATCH 28/46] feat: Group API in core and extensions (#23) Signed-off-by: Thomas Poignant --- service/openapi.yaml | 93 ++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 2fa8cb5..448d267 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -13,46 +13,24 @@ info: security: - ApiKeyAuth: [ ] - BearerAuth: [ ] +tags: + - name: OFREP Core + description: | + **Required**: Core APIs to implement to support OFREP. + *This is the minimum set of APIs required for a flag management system to be OFREP compatible.* + - name: OFREP Extensions + description: | + **Optional**: Extension APIs to provide full support for OFREP. + *These APIs are utilized by the providers to enhance the OFREP experience.* + paths: - /ofrep/v1/configuration: - get: - summary: OFREP provider configuration - description: OFREP configuration to provide information about the remote flag management system, to configure the OpenFeature SDK providers. This endpoint will be called during the initialization of the provider. - parameters: - - in: header - name: If-None-Match - description: The request will be processed only if ETag doesn't match any of the values listed. - schema: - type: string - required: false - responses: - '200': - description: OFREP metadata response - headers: - ETag: - schema: - type: string - description: Entity tag used for cache validation - content: - application/json: - schema: - $ref: '#/components/schemas/configurationResponse' - '304': - description: Flag Management System Metadata is not modified - '401': - description: Unauthorized - You need credentials to access the API - '403': - description: Forbidden - You are not authorized to access the API - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/generalErrorResponse' /ofrep/v1/evaluate/flags/{key}: post: + tags: [OFREP core] summary: OFREP single flag evaluation contract - description: OFREP single flag evaluation request + description: | + OFREP single flag evaluation request. + The endpoint is called by the server providers to perform single flag evaluation. parameters: - name: key in: path @@ -105,8 +83,11 @@ paths: $ref: '#/components/schemas/generalErrorResponse' /ofrep/v1/evaluate/flags: post: + tags: [OFREP core] summary: OFREP bulk flag evaluation contract - description: OFREP bulk evaluation request + description: | + OFREP bulk evaluation request. + The endpoint is called by the client providers to perform single flag evaluation. parameters: - in: header name: If-None-Match @@ -159,6 +140,44 @@ paths: application/json: schema: $ref: '#/components/schemas/generalErrorResponse' + /ofrep/v1/configuration: + get: + tags: [OFREP extensions] + summary: OFREP provider configuration + description: | + OFREP configuration is used to supply information about the remote flag management system and to set up the OpenFeature SDK providers. + The providers will contact this endpoint only if the client has opted in. + parameters: + - in: header + name: If-None-Match + description: The request will be processed only if ETag doesn't match any of the values listed. + schema: + type: string + required: false + responses: + '200': + description: OFREP metadata response + headers: + ETag: + schema: + type: string + description: Entity tag used for cache validation + content: + application/json: + schema: + $ref: '#/components/schemas/configurationResponse' + '304': + description: Flag Management System Metadata is not modified + '401': + description: Unauthorized - You need credentials to access the API + '403': + description: Forbidden - You are not authorized to access the API + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/generalErrorResponse' components: securitySchemes: BearerAuth: From 451caac82b93b6083713fcbe5afe750e7ff7b226 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Tue, 11 Jun 2024 09:45:17 +0200 Subject: [PATCH 29/46] feat!: Reverse the logic for supportedTypes (#24) * feat!: Reverse the logic for supportedTypes Signed-off-by: Thomas Poignant * fix Signed-off-by: Thomas Poignant --------- Signed-off-by: Thomas Poignant --- service/openapi.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 448d267..cbda1b2 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -358,14 +358,17 @@ components: type: object description: Configurations specific for flag evaluations in OFREP provider implementation properties: - unsupportedTypes: - description: A list of unsupported types by the flag management system. Evaluating a flag of a listed type through OFREP provider will result in an error and yield the default value. + supportedTypes: + description: | + Evaluating a flag of unlisted type through the OFREP provider will result in an error and yield the default value. + However, when supportedTypes is undefined/empty, provider assumes that all flag evaluation types are supported by the flag management system type: array items: type: string enum: [int, float, string, boolean, object] examples: - ["object", "int", "float"] + - null featureCacheInvalidation: type: object description: Configuration for the cache cacheInvalidation From d87f64badc436b8fb53f477ff68b0f7b136f5293 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Thu, 20 Jun 2024 18:08:40 +0200 Subject: [PATCH 30/46] doc: adding providers link (#26) Signed-off-by: Thomas Poignant --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a8ae411..fec34e8 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ We value the following: - low latency of flag evaluations - portability and interoperability between runtimes and languages (should be usable in web, mobile devices, and server) - minimum reliance on dependencies (leverage ubiquitous, native transports and encodings where possible) + +## Providers +- [GO](https://github.com/open-feature/go-sdk-contrib/tree/main/providers/ofrep) +- [JS Server](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/ofrep) +- [JS Web](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/ofrep-web) ## Contribution If you are interested about the OpenFeature Remote Evaluation Protocol you can join the [`#openfeature-remote-evaluation-protocol`](https://cloud-native.slack.com/archives/C066A48LK35) slack channel on the [CNCF Slack](https://communityinviter.com/apps/cloud-native/cncf). + From 87cbb1ab985f135d0f19d3db718767f958e4a80c Mon Sep 17 00:00:00 2001 From: Kavindu Dodanduwa Date: Tue, 25 Jun 2024 12:05:32 -0700 Subject: [PATCH 31/46] make flags property mandatory for bulk evaluation success response (#27) Signed-off-by: Kavindu Dodanduwa Signed-off-by: Thomas Poignant --- service/openapi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/openapi.yaml b/service/openapi.yaml index cbda1b2..6433059 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -198,6 +198,8 @@ components: bulkEvaluationSuccess: description: Success response for the bulk evaluation request type: object + required: + - flags properties: flags: type: array From e8a0ba1a7f48c0c205cd3cfa0e2db251b738942b Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 5 Jul 2024 05:19:04 +0200 Subject: [PATCH 32/46] feat: Typo in header name (#28) Signed-off-by: Thomas Poignant --- service/openapi.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 6433059..865e02b 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -68,7 +68,7 @@ paths: '429': description: Rate limit reached on the Flag Management System headers: - Retry-Later: + Retry-After: description: Indicates when to retry the request again schema: type: string @@ -392,4 +392,4 @@ components: examples: - 60000 required: - - name \ No newline at end of file + - name From dcb3fd37a50f41db693d12424c05543d7b1be6a4 Mon Sep 17 00:00:00 2001 From: Michael Beemer Date: Fri, 26 Jul 2024 11:57:23 -0400 Subject: [PATCH 33/46] add optional targeting key property (#30) Signed-off-by: Michael Beemer Signed-off-by: Thomas Poignant --- service/openapi.yaml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 865e02b..d7ee822 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -29,7 +29,7 @@ paths: tags: [OFREP core] summary: OFREP single flag evaluation contract description: | - OFREP single flag evaluation request. + OFREP single flag evaluation request. The endpoint is called by the server providers to perform single flag evaluation. parameters: - name: key @@ -86,8 +86,8 @@ paths: tags: [OFREP core] summary: OFREP bulk flag evaluation contract description: | - OFREP bulk evaluation request. - The endpoint is called by the client providers to perform single flag evaluation. + OFREP bulk evaluation request. + The endpoint is called by the client providers to evaluate all flags at once. parameters: - in: header name: If-None-Match @@ -145,7 +145,7 @@ paths: tags: [OFREP extensions] summary: OFREP provider configuration description: | - OFREP configuration is used to supply information about the remote flag management system and to set up the OpenFeature SDK providers. + OFREP configuration is used to supply information about the remote flag management system and to set up the OpenFeature SDK providers. The providers will contact this endpoint only if the client has opted in. parameters: - in: header @@ -290,6 +290,13 @@ components: context: type: object description: Context information for flag evaluation + properties: + targetingKey: + type: string + description: A string logically identifying the subject of evaluation (end-user, service, etc). + examples: + - user-123 + additionalProperties: true booleanFlag: description: A boolean typed flag value properties: From fef90436bffc77ec9d6d2f4dc2ce4738325cc84f Mon Sep 17 00:00:00 2001 From: Roman Dmytrenko Date: Fri, 6 Sep 2024 22:21:33 +0300 Subject: [PATCH 34/46] fix: use correct header name for 429 bulk response (#32) Signed-off-by: Roman Dmytrenko Signed-off-by: Thomas Poignant --- service/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index d7ee822..1e758ea 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -127,7 +127,7 @@ paths: '429': description: Rate limit reached on the Flag Management System headers: - Retry-Later: + Retry-After: description: Indicates when to retry the request again schema: type: string From 327d3cc7a601904e5226d5d91d76cfd424d95b57 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Wed, 22 Jan 2025 20:53:13 +0100 Subject: [PATCH 35/46] feat: Specify caching for OFREP in server providers (#17) Signed-off-by: Thomas Poignant Signed-off-by: Kavindu Dodanduwa Co-authored-by: Kavindu Dodanduwa Co-authored-by: Todd Baert Signed-off-by: Thomas Poignant --- service/openapi.yaml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 1e758ea..9f3b3b2 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -48,7 +48,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/evaluationSuccess' + $ref: '#/components/schemas/serverEvaluationSuccess' '400': description: Bad evaluation request content: @@ -223,6 +223,13 @@ components: properties: context: $ref: '#/components/schemas/context' + serverEvaluationSuccess: + allOf: + - $ref: "#/components/schemas/evaluationSuccess" + - properties: + cacheable: + type: boolean + description: Let the provider know that this flag evaluation can be cached evaluationSuccess: description: Flag evaluation success response. allOf: @@ -363,6 +370,8 @@ components: $ref: '#/components/schemas/featureCacheInvalidation' flagEvaluation: $ref: '#/components/schemas/flagEvaluation' + caching: + $ref: '#/components/schemas/featureCaching' flagEvaluation: type: object description: Configurations specific for flag evaluations in OFREP provider implementation @@ -400,3 +409,15 @@ components: - 60000 required: - name + featureCaching: + type: object + description: Configuration of the caching mechanism in the provider (used by server providers) + properties: + enabled: + type: boolean + description: set to true if you want the provider to cache the evaluation results + ttl: + type: number + examples: + - 1000 + description: number (in millisecond) to wait before invalidating the cache. If we have cacheInvalidation enabled, the cache can also be evicted if a configuration change happen. From 9ca3e262ad10fc350927e06efa3ae725ef0a2368 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 31 Jan 2025 13:25:23 -0500 Subject: [PATCH 36/46] feat: add flag set metadata for bulk response and failures (#34) Signed-off-by: Todd Baert Signed-off-by: Thomas Poignant --- service/openapi.yaml | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 9f3b3b2..75c5642 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -207,6 +207,12 @@ components: oneOf: - $ref: "#/components/schemas/evaluationSuccess" - $ref: '#/components/schemas/evaluationFailure' + metadata: + $ref: "#/components/schemas/metadata" + description: Arbitrary metadata for the flag set, useful for telemetry and documentary purposes. + examples: + - flagSetId: holidaySales + version: v12 bulkEvaluationFailure: description: Bulk evaluation failure response properties: @@ -244,13 +250,10 @@ components: type: string description: Variant of the evaluated flag value metadata: - type: object - additionalProperties: - oneOf: - - type: boolean - - type: string - - type: number - description: Arbitrary metadata supporting flag evaluation + allOf: + - $ref: "#/components/schemas/metadata" + - $ref: "#/components/schemas/flagMetadataDescription" + - $ref: "#/components/schemas/flagMetadataExamples" - oneOf: - $ref: "#/components/schemas/booleanFlag" - $ref: "#/components/schemas/stringFlag" @@ -268,6 +271,11 @@ components: description: OpenFeature compatible error code. See https://openfeature.dev/specification/types#error-code errorDetails: $ref: '#/components/schemas/errorDetails' + metadata: + allOf: + - $ref: "#/components/schemas/metadata" + - $ref: "#/components/schemas/flagMetadataDescription" + - $ref: "#/components/schemas/flagMetadataExamples" required: - key - errorCode @@ -281,6 +289,11 @@ components: enum: [ FLAG_NOT_FOUND ] errorDetails: $ref: '#/components/schemas/errorDetails' + metadata: + allOf: + - $ref: "#/components/schemas/metadata" + - $ref: "#/components/schemas/flagMetadataDescription" + - $ref: "#/components/schemas/flagMetadataExamples" required: - key - errorCode @@ -421,3 +434,17 @@ components: examples: - 1000 description: number (in millisecond) to wait before invalidating the cache. If we have cacheInvalidation enabled, the cache can also be evicted if a configuration change happen. + metadata: + type: object + additionalProperties: + oneOf: + - type: boolean + - type: string + - type: number + flagMetadataExamples: + examples: + - team: ecommerce + businessPurpose: experiment + flagMetadataDescription: + description: Arbitrary metadata for the flag, useful for telemetry and documentary purposes. + From 0900b1833197711db5a6638729f88158a4cf5f28 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 7 Feb 2025 17:25:15 +0100 Subject: [PATCH 37/46] Fixups: Add operationIds, remove invalid property, fix tag casing (#35) Signed-off-by: Honza Dvorsky Signed-off-by: Thomas Poignant --- service/openapi.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 75c5642..7d26574 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -26,11 +26,12 @@ tags: paths: /ofrep/v1/evaluate/flags/{key}: post: - tags: [OFREP core] + tags: [OFREP Core] summary: OFREP single flag evaluation contract description: | OFREP single flag evaluation request. The endpoint is called by the server providers to perform single flag evaluation. + operationId: evaluateFlag parameters: - name: key in: path @@ -83,11 +84,12 @@ paths: $ref: '#/components/schemas/generalErrorResponse' /ofrep/v1/evaluate/flags: post: - tags: [OFREP core] + tags: [OFREP Core] summary: OFREP bulk flag evaluation contract description: | OFREP bulk evaluation request. The endpoint is called by the client providers to evaluate all flags at once. + operationId: evaluateFlagsBulk parameters: - in: header name: If-None-Match @@ -142,11 +144,12 @@ paths: $ref: '#/components/schemas/generalErrorResponse' /ofrep/v1/configuration: get: - tags: [OFREP extensions] + tags: [OFREP Extensions] summary: OFREP provider configuration description: | OFREP configuration is used to supply information about the remote flag management system and to set up the OpenFeature SDK providers. The providers will contact this endpoint only if the client has opted in. + operationId: getConfiguration parameters: - in: header name: If-None-Match @@ -420,8 +423,6 @@ components: The provider should ensure not to set any polling value under this minimum. examples: - 60000 - required: - - name featureCaching: type: object description: Configuration of the caching mechanism in the provider (used by server providers) From f610bf76edf2c9cf7f3c5b686f8af2e49e788a10 Mon Sep 17 00:00:00 2001 From: Michael Beemer Date: Fri, 7 Feb 2025 11:54:09 -0500 Subject: [PATCH 38/46] ci: switch OpenAPI validators (#36) Signed-off-by: Michael Beemer Signed-off-by: Thomas Poignant --- .github/workflows/spec-validate.yaml | 8 ++++---- .spectral.yaml | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .spectral.yaml diff --git a/.github/workflows/spec-validate.yaml b/.github/workflows/spec-validate.yaml index 2a990b3..c961089 100644 --- a/.github/workflows/spec-validate.yaml +++ b/.github/workflows/spec-validate.yaml @@ -1,7 +1,7 @@ name: OpenAPI spec validation action on: - pull_request_target: + pull_request: types: - opened - edited @@ -15,6 +15,6 @@ jobs: uses: actions/checkout@v4 - name: Run OpenAPI spec validation - run: | - docker run --rm -v $PWD:/spec redocly/cli lint ./service/openapi.yaml - echo "{exit_code}={$?}" >> $GITHUB_STATE \ No newline at end of file + uses: stoplightio/spectral-action@v0.8.11 + with: + file_glob: 'service/openapi.yaml' diff --git a/.spectral.yaml b/.spectral.yaml new file mode 100644 index 0000000..a8d6190 --- /dev/null +++ b/.spectral.yaml @@ -0,0 +1,2 @@ +extends: + - spectral:oas From ec48b7626f5ed7d6486c8d958f9c98529ea8622a Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 9 May 2025 10:42:16 +0200 Subject: [PATCH 39/46] fix: address style issues Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 58 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 0f6db48..1d22eaf 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -2,27 +2,27 @@ OpenFeature Remote Evaluation Protocol (OFREP) is an API specification for feature flagging that allows the use of generic providers to connect to any feature flag management systems that supports the protocol. -In this document, we will specify how to write an OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operate in the context of a single user. -We will keep the document language agnostic. +In this document, we will specify how to write an OFREP provider using the [static-context-paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm) that is used on client side applications typically operating in the context of a single user. **Pre-requisite:** - Understanding of [general provider concepts](https://openfeature.dev/docs/reference/concepts/provider/) - Understanding of the [OFREP](../../README.md) - Understanding of the [OFREP OpenAPI specification](../../service/openapi.yaml) -## Constructor -An implementation of an OFREP client provider should allow in the creation of the provider to take as options: +## Configuration +An OFREP client provider implementation must, at the time of creation, accept at least these options - `baseURL`: The base URL of the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system). - This should be the base of the URL pointing before the `/ofrep` namespace of the API. + This must be the base of the URL pointing before the `/ofrep` namespace of the API. - In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. -- `headers`: The headers to use when calling the OFREP endpoints *(ex:`Authorization`, Custom headers, etc ...)*. -- `pollInterval`: The polling interval defining when to call again the flag management system. - - If `pollInterval` is equals to 0, polling will be disabled. +- `headers`: The headers to use when calling the OFREP endpoints *(e.g.:`Authorization`, Custom headers, etc ...)*. +- `pollInterval`: The polling interval defining how often to update the cached flags in the provider. + - If `pollInterval` is equal to 0, polling will be disabled. + - See [polling section](#polling) for more details. ## Initialize the provider -An implementation of an OFREP client provider should start with an initialization of the provider. +The following describes the [initialization](https://openfeature.dev/specification/sections/providers#24-initialization) of an OFREP provider: -The `initialize()` function should follow those steps: +The `initialize()` function must follow those steps: 1. Make a POST request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body. **Request body example**: @@ -39,24 +39,24 @@ The `initialize()` function should follow those steps: - If the endpoint returns an error, the `initialize()` function must error and exit. - If the request is successful: - - We should store in a local cache all of the flags evaluation details returned by the API. - - We should store the `ETag` header in the provider to be able to send it back later. -2. If `pollInterval` is more than `0`, we should start a polling mechanism to periodically check for flag changes *([See polling section](#polling))*. + - Thew provider must store in a local cache all of the flags evaluation details returned by the API. + - Thew provider must store the `ETag` header in the provider to be able to send it back later. +2. If `pollInterval` is more than `0`, a polling mechanism must be started to periodically check for flag changes *([See polling section](#polling))*. ## Evaluation -The evaluation should not perform any remote API calls and use the local cache. +The evaluation MUST not perform any remote API calls and use the local cache. -When calling an evaluation function, the provider should follow those steps: +When calling an evaluation function, the provider must follow those steps: 1. Retrieve the evaluation details for this flag from the local cache. -2. If you are not able to find the flag in the local cache, we should return an `FLAG_NOT_FOUND` error. -3. If the evaluation details contains an error, we should map the error in provider to an OpenFeature error and return it. -4. If the value of the evaluation details in the cache has a different type than the one expected by the evaluation function you should return a `TYPE_MISMATCH` error. -5. If the evaluation details retrieved from the cache is successful, you should return the evaluation details. +2. If the provider is not able to find the flag in the local cache, it must return an `FLAG_NOT_FOUND` error. +3. If the evaluation details contains an error, the provider must map the error in provider to an OpenFeature error and return it. +4. If the value of the evaluation details in the cache has a different type than the one expected by the evaluation function the provider must return a `TYPE_MISMATCH` error. +5. If the evaluation details retrieved from the cache is successful, the provider returns the evaluation details. ```mermaid flowchart TD - A[evaluation function] --> D{Do we have an entry in cache for this flag key ?} + A[evaluation function] --> D{Does the provider have an entry in cache for this flag key ?} D --> |NO| E(return a FLAG_NOT_FOUND error) D --> |YES| F{Is cached evaluation details in error?} F --> |YES| G(Map the error as an OpenFeature provider error and return it) @@ -69,19 +69,19 @@ flowchart TD The polling system will make a `POST` request periodically to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if there is a change in the flags evaluation to be able to store it. The goal is to be able to know when a flag has changed for this evaluation context. -When you call the API if an `ETag` of a previous evaluation is available it is required to add the header `If-None-Match` with the `ETag` value to the `POST` request. +When calling the API, if an `ETag` of a previous evaluation is available, it must be added to the [`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-None-Match) header with the `ETag` as value. -When calling the API you can have those response codes: -- `304`: Means that your current cache is up-to-date. -- `401`, `403`: The provider is not authorized to call the OFREP API. In that situation we should return an error and stop polling. -- `429`: You have reached the rate limit of the flag management system. In that situation the provider should read the `Retry-After` header from the response and ensure that we are not calling the endpoint again before this date. -- `200`: The cache is outdated. In that situation we should: +When calling the API the provider can receive those response codes: +- `304`: Means that the current cache is up-to-date. +- `401`, `403`: The provider is not authorized to call the OFREP API. In that situation the provider should emit a `fatal` event and stop the polling mechanism. The provider will be in a [terminal error state](https://openfeature.dev/specification/sections/flag-evaluation#requirement-177). +- `429`: The provider has reached the rate limit of the flag management system. In that situation the provider should read the `Retry-After` header from the response and ensure that no call to the endpoint happened before this date. +- `200`: The cache is outdated. In that situation the provider must: 1. Clear the actual cache. - 2. Cache the evaluation details received. + 2. Cache the new evaluation details received. 3. Read the `ETag` header and store it for future call the API. 4. Emit an `ConfigurationChanged` event containing in the `flagsChanged` field, a list of the flag key that have changed. ## Change context -In the client providers, a change of context should be handle to retrieve a new version of the cache. +In the client providers, a change of context should be handle to retrieve a new version of the cache, it is called that the [Provider context reconciliation](https://openfeature.dev/specification/sections/providers/#26-provider-context-reconciliation). -When the function `onContextChange` is called, we should call the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if we have to update the cache. In that situation we should handle the response the same way we are doing it for polling. \ No newline at end of file +When the function `onContextChange` is called, the provider should call the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if the cache needs to be updated. In that situation the provider must handle the response the same way it does for polling. \ No newline at end of file From 93b2becc38626187568d1764212d2a3e849f2149 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Sat, 10 May 2025 17:55:05 +0200 Subject: [PATCH 40/46] Update static-context-provider.md Co-authored-by: Lukas Reining Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index 1d22eaf..df98988 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -39,8 +39,8 @@ The `initialize()` function must follow those steps: - If the endpoint returns an error, the `initialize()` function must error and exit. - If the request is successful: - - Thew provider must store in a local cache all of the flags evaluation details returned by the API. - - Thew provider must store the `ETag` header in the provider to be able to send it back later. + - The provider must store in a local cache all of the flags evaluation details returned by the API. + - The provider must store the `ETag` header in the provider to be able to send it back later. 2. If `pollInterval` is more than `0`, a polling mechanism must be started to periodically check for flag changes *([See polling section](#polling))*. ## Evaluation From b443840475ce5524ad333fd9d8ac24ea515d7a0c Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Sat, 10 May 2025 17:55:21 +0200 Subject: [PATCH 41/46] Update static-context-provider.md Co-authored-by: Lukas Reining Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index df98988..cf69a4b 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -66,8 +66,8 @@ flowchart TD ``` ## Polling -The polling system will make a `POST` request periodically to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if there is a change in the flags evaluation to be able to store it. -The goal is to be able to know when a flag has changed for this evaluation context. +The polling system must periodically (interval defined by `pollingInterval`) make a `POST` request to the `/ofrep/v1/evaluate/flags` endpoint with the evaluation context in the body to check if there is a change in the flags evaluation to be able to store it. +If the flags changed, the provider cache for evaluation is updated. When calling the API, if an `ETag` of a previous evaluation is available, it must be added to the [`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-None-Match) header with the `ETag` as value. From 3f5fbf789eee1723fcd4086a2601333768dd5d30 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Sat, 10 May 2025 18:20:30 +0200 Subject: [PATCH 42/46] adding timeout Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 1 + 1 file changed, 1 insertion(+) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index cf69a4b..a8badf7 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -18,6 +18,7 @@ An OFREP client provider implementation must, at the time of creation, accept at - `pollInterval`: The polling interval defining how often to update the cached flags in the provider. - If `pollInterval` is equal to 0, polling will be disabled. - See [polling section](#polling) for more details. +- `timeout`: This specifies the duration to wait for a response before canceling the HTTP request. If no value is provided, a default timeout of `10 seconds` must be applied. ## Initialize the provider The following describes the [initialization](https://openfeature.dev/specification/sections/providers#24-initialization) of an OFREP provider: From b8fe90139932eaddac0cfc7b26e380ad24c9fb1f Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Mon, 12 May 2025 09:25:57 +0200 Subject: [PATCH 43/46] feat: Server provider guidelines (#42) * feat: Server provider guideline Signed-off-by: Thomas Poignant * adding timeout Signed-off-by: Thomas Poignant --------- Signed-off-by: Thomas Poignant --- guideline/dynamic-context-provider.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 guideline/dynamic-context-provider.md diff --git a/guideline/dynamic-context-provider.md b/guideline/dynamic-context-provider.md new file mode 100644 index 0000000..031c1d0 --- /dev/null +++ b/guideline/dynamic-context-provider.md @@ -0,0 +1,40 @@ +# Creating an OFREP server provider + +OpenFeature Remote Evaluation Protocol (OFREP) is an API specification for feature flagging that allows the use of generic providers to connect to any feature flag management systems that supports the protocol. + +In this document, we will specify how to write an OFREP provider using the [dynamic-context-paradigm](https://openfeature.dev/specification/glossary/#dynamic-context-paradigm) that is used on server side applications typically operate in the context of multiple users. + +**Pre-requisite:** +- Understanding of [general provider concepts](https://openfeature.dev/docs/reference/concepts/provider/) +- Understanding of the [OFREP](../../README.md) +- Understanding of the [OFREP OpenAPI specification](../../service/openapi.yaml) + +## Configuration +An OFREP server provider implementation must, at the time of creation, accept at least these options +- `baseURL`: The base URL of the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system). + This must be the base of the URL pointing before the `/ofrep` namespace of the API. + - In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. +- `headers`: The headers to use when calling the OFREP endpoints *(e.g.:`Authorization`, Custom headers, etc ...)*. +- `timeout`: This specifies the duration to wait for a response before canceling the HTTP request. If no value is provided, a default timeout of `10 seconds` must be applied. + +## Evaluation +When an evaluation function is called the server provider will make a `POST` request to the `/ofrep/v1/evaluate/flags/{key}` endpoint *(where `{key}` is the flag name), with the evaluation context in the body. + +**Request body example**: +```json +{ + "context": { + "targetingKey": "f021d0f9-33b7-4b22-b0bd-9fec66ba1d7d", + "firstname": "foo", + "lastname": "bar", + "email": "foo.bar@ofrep.dev" + } +} +``` + +When calling the API the provider can receive those response codes: +- `400`: Bad evaluation request, this means that the flag management system has returned an error during the evaluation. In that situation the provider should map the error returned to an OpenFeature Error and return it. +- `401`, `403`: The provider is not authorized to call the OFREP API. In that situation the provider should return an error. +- `404`: Flag not found, this means that the flag management system does not have a flag for this flag key. In that situation the provider should return a `FLAG_NOT_FOUND` error. +- `429`: The provider has reached the rate limit of the flag management system. In that situation the provider should read the `Retry-After` header from the response and ensure that no call to the endpoint happened before this date. Every evaluation happening before the retry after date should error and return the default value. +- `200`: The flag management system has returned a valid evaluation details for this flag. In that situation the provider must map the API response into an evaluation details response, and return it. From ac41c3cfef54d1cc5ae05dd1268cdf0f55d47eca Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Mon, 10 Jun 2024 09:27:48 +0200 Subject: [PATCH 44/46] feat: Group API in core and extensions (#23) Signed-off-by: Thomas Poignant --- service/openapi.yaml | 110 ------------------------------------------- 1 file changed, 110 deletions(-) diff --git a/service/openapi.yaml b/service/openapi.yaml index 7d26574..4c6a282 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -18,10 +18,6 @@ tags: description: | **Required**: Core APIs to implement to support OFREP. *This is the minimum set of APIs required for a flag management system to be OFREP compatible.* - - name: OFREP Extensions - description: | - **Optional**: Extension APIs to provide full support for OFREP. - *These APIs are utilized by the providers to enhance the OFREP experience.* paths: /ofrep/v1/evaluate/flags/{key}: @@ -142,45 +138,6 @@ paths: application/json: schema: $ref: '#/components/schemas/generalErrorResponse' - /ofrep/v1/configuration: - get: - tags: [OFREP Extensions] - summary: OFREP provider configuration - description: | - OFREP configuration is used to supply information about the remote flag management system and to set up the OpenFeature SDK providers. - The providers will contact this endpoint only if the client has opted in. - operationId: getConfiguration - parameters: - - in: header - name: If-None-Match - description: The request will be processed only if ETag doesn't match any of the values listed. - schema: - type: string - required: false - responses: - '200': - description: OFREP metadata response - headers: - ETag: - schema: - type: string - description: Entity tag used for cache validation - content: - application/json: - schema: - $ref: '#/components/schemas/configurationResponse' - '304': - description: Flag Management System Metadata is not modified - '401': - description: Unauthorized - You need credentials to access the API - '403': - description: Forbidden - You are not authorized to access the API - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/generalErrorResponse' components: securitySchemes: BearerAuth: @@ -369,72 +326,6 @@ components: errorDetails: type: string description: An error description for logging or other needs - configurationResponse: - description: OFREP metadata response - properties: - name: - type: string - description: name of the flag management system - examples: - - flagd - - go-feature-flag - capabilities: - type: object - description: Capabilities of the flag management system and how to configure them in the provider. - properties: - cacheInvalidation: - $ref: '#/components/schemas/featureCacheInvalidation' - flagEvaluation: - $ref: '#/components/schemas/flagEvaluation' - caching: - $ref: '#/components/schemas/featureCaching' - flagEvaluation: - type: object - description: Configurations specific for flag evaluations in OFREP provider implementation - properties: - supportedTypes: - description: | - Evaluating a flag of unlisted type through the OFREP provider will result in an error and yield the default value. - However, when supportedTypes is undefined/empty, provider assumes that all flag evaluation types are supported by the flag management system - type: array - items: - type: string - enum: [int, float, string, boolean, object] - examples: - - ["object", "int", "float"] - - null - featureCacheInvalidation: - type: object - description: Configuration for the cache cacheInvalidation - properties: - polling: - $ref: '#/components/schemas/featureCacheInvalidationPolling' - featureCacheInvalidationPolling: - type: object - description: Configuration of the polling for the featureCacheInvalidation - properties: - enabled: - type: boolean - description: set to true if the remote flag management system is supporting polling - minPollingIntervalMs: - type: number - description: | - Minimum polling interval (in millisecond) supported by the flag management system. - The provider should ensure not to set any polling value under this minimum. - examples: - - 60000 - featureCaching: - type: object - description: Configuration of the caching mechanism in the provider (used by server providers) - properties: - enabled: - type: boolean - description: set to true if you want the provider to cache the evaluation results - ttl: - type: number - examples: - - 1000 - description: number (in millisecond) to wait before invalidating the cache. If we have cacheInvalidation enabled, the cache can also be evicted if a configuration change happen. metadata: type: object additionalProperties: @@ -448,4 +339,3 @@ components: businessPurpose: experiment flagMetadataDescription: description: Arbitrary metadata for the flag, useful for telemetry and documentary purposes. - From 07df22636c52c2ca44ef12464d882b5eb6f25da9 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 16 May 2025 18:36:06 +0200 Subject: [PATCH 45/46] Update guideline/static-context-provider.md Co-authored-by: Michael Beemer Signed-off-by: Thomas Poignant Signed-off-by: Thomas Poignant --- guideline/static-context-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guideline/static-context-provider.md b/guideline/static-context-provider.md index a8badf7..11d8fd4 100644 --- a/guideline/static-context-provider.md +++ b/guideline/static-context-provider.md @@ -18,7 +18,7 @@ An OFREP client provider implementation must, at the time of creation, accept at - `pollInterval`: The polling interval defining how often to update the cached flags in the provider. - If `pollInterval` is equal to 0, polling will be disabled. - See [polling section](#polling) for more details. -- `timeout`: This specifies the duration to wait for a response before canceling the HTTP request. If no value is provided, a default timeout of `10 seconds` must be applied. +- `timeoutMs`: This specifies the duration to wait for a response before canceling the HTTP request. If no value is provided, a default timeout of `10_000 milliseconds` must be applied. ## Initialize the provider The following describes the [initialization](https://openfeature.dev/specification/sections/providers#24-initialization) of an OFREP provider: From 2502795c85b1198105828ca50b191c4d09e462d4 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Fri, 16 May 2025 18:45:46 +0200 Subject: [PATCH 46/46] Update openapi.yaml Signed-off-by: Thomas Poignant --- service/openapi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/service/openapi.yaml b/service/openapi.yaml index 4c6a282..00804ab 100644 --- a/service/openapi.yaml +++ b/service/openapi.yaml @@ -339,3 +339,4 @@ components: businessPurpose: experiment flagMetadataDescription: description: Arbitrary metadata for the flag, useful for telemetry and documentary purposes. +