diff --git a/.adr-dir b/.adr-dir new file mode 100644 index 0000000..90a6e9b --- /dev/null +++ b/.adr-dir @@ -0,0 +1 @@ +./service/adrs diff --git a/.gitignore b/.gitignore index 485dee6..3c93ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +.DS_Store +.vscode .idea +.gen \ No newline at end of file diff --git a/service/adrs/0001-json-over-http-for-the-protocol.md b/service/adrs/0001-json-over-http-for-the-protocol.md new file mode 100644 index 0000000..0b90738 --- /dev/null +++ b/service/adrs/0001-json-over-http-for-the-protocol.md @@ -0,0 +1,24 @@ +# 1. JSON over HTTP for the protocol + +Date: 2024-02-29 + +## Status + +Accepted + +## Context + +OFREP needs a network protocol and serialization format for the feature flag evaluation information. +The protocol and serialization format must be supported in web, mobile and server environments. + +## Decision + +Technology selection was based on the [survey feedback from vendors](https://docs.google.com/forms/d/1NzqKx57XvRK_2lRQOFCRmF5exet6f15-sCjdEy0HCS8#responses). +The majority of the vendors were using JSON & HTTP in their current implementations. +Based on this, it was decided to use JSON over HTTP for OFREP. + +## Consequences + +- Web, mobile and server environments have great support for JSON over HTTP +- Many developers are familiar with this technology which may reduce the implementation burden +- Most vendors are already using this technology, hence it will be easier for them to adopt OFREP diff --git a/service/adrs/0002-two-evaluation-endpoints.md b/service/adrs/0002-two-evaluation-endpoints.md new file mode 100644 index 0000000..2e1f0f5 --- /dev/null +++ b/service/adrs/0002-two-evaluation-endpoints.md @@ -0,0 +1,28 @@ +# 2. Two evaluation endpoints + +Date: 2024-03-01 + +## Status + +Accepted + +## Context + +Evaluating flags in the static context paradigm, is often done by bulk evaluating flags and updating them if needed. +For example, [frontend would most times load all flags](https://openfeature.dev/blog/catering-to-the-client-side). +And the payload is cached to prevent loading times and to avoid overloading the feature flagging service. + +In the dynamic context paradigm, flags are usually evaluated on demand. +For example, backend service often evaluates flags based on the information present in the specific request. +This makes bulk evaluation less favourable for dynamic context paradigm. + +## Decision + +OFREP contains two endpoints, one for individual flag evaluations and the other for bulk evaluations. +Individual flag evaluation is preferred for the dynamic context paradigm whereas the bulk evaluation is preferred for static context paradigm. + +## Consequences + +- OFREP supports single and bulk evaluations +- Each endpoint might evolve to better serve the specific OpenFeature paradigm +- It might become challenging to prevent the endpoints from diverging diff --git a/service/adrs/0003-no-flag-type-in-request-or-response.md b/service/adrs/0003-no-flag-type-in-request-or-response.md new file mode 100644 index 0000000..8f9d743 --- /dev/null +++ b/service/adrs/0003-no-flag-type-in-request-or-response.md @@ -0,0 +1,33 @@ +# 3. No flag type in request or response + +Date: 2024-03-01 + +## Status + +Accepted + +## Context + +Feature flags in OpenFeature can have several different types. +Often you see `boolean | number | string | structure` and often specific number types like `integer | float`. +Details can be seen in the [OF specification](https://openfeature.dev/specification/types). + +When evaluating a flag via OFREP, the provider implementation will have to parse the flag value from the JSON response. +There are different approaches to this. Some feature flagging services define an expected flag type in the request. +Some include the flag type or even a schema in the response and some do not specify any type either in request or response. + +## Decision + +In the community meeting on 2024-02-29, it was concluded **not** to include type details in any payload. +The reason for this is to keep the API simple until there is a need for improvements. + +Provider implementations are expected to infer the types from the JSON response. +Given that OpenFeature evaluation supports primitive types, value conversions may focus on evaluation type and deserialized JSON value type. +To differentiate integers and floats, the provider implementations will need to enforce JSON number parsing rules in the respective language. + +## Consequences + +- OFREP stays simple +- Parsing flag types can be done by using the JSON value type +- Parsing language specific types like floats will be up to the provider implementations +- Not having the request type in the request results in the feature flagging service, not knowing when there is a flag type mismatch diff --git a/service/adrs/0004-no-flag-filter-in-bulk-evaluation-request.md b/service/adrs/0004-no-flag-filter-in-bulk-evaluation-request.md new file mode 100644 index 0000000..a4c2f99 --- /dev/null +++ b/service/adrs/0004-no-flag-filter-in-bulk-evaluation-request.md @@ -0,0 +1,23 @@ +# 4. No flag filter in bulk evaluation request + +Date: 2024-03-01 + +## Status + +Accepted + +## Context + +There can be cases where bulk evaluation evaluates and returns hundreds or thousands of flags. +Some feature flagging systems define filters that specify which flags should be bulk evaluated. + +## Decision + +OFREP does not specify a `flags` property that specifies the flags to be bulk evaluated. +For systems that rely on a list of flags, there is an alternative to include them through the `context` object. + +## Consequences + +- There is no standardized way of filtering flags + - Flags can still be filtered via the evaluation context +- OFREP stays simple and the evaluation only depends on context and authorization diff --git a/service/adrs/0005-polling-for-bulk-evaluation-changes.md b/service/adrs/0005-polling-for-bulk-evaluation-changes.md new file mode 100644 index 0000000..de2afd8 --- /dev/null +++ b/service/adrs/0005-polling-for-bulk-evaluation-changes.md @@ -0,0 +1,38 @@ +# 5. Polling for bulk evaluation changes + +Date: 2024-03-01 + +## Status + +Accepted + +## Context + +In most systems, feature flags can be changed at any time. +This is a challenge for bulk evaluation where the typical use case is caching the evaluated flags and evaluating from the cache. + +Many feature flagging systems provide ways to inform clients about changes in feature flag values. +This can reach from polling clients over to server-sent events and websockets. + +Some providers also define polling endpoints that list changed feature flags instead of polling the evaluation endpoint. + +The [vendor survey](https://docs.google.com/forms/d/1NzqKx57XvRK_2lRQOFCRmF5exet6f15-sCjdEy0HCS8#responses) showed that many vendors do simple polling from clients or SSE. + +## Decision + +OFREP intends provider implementations to do polling. For now no streaming like SSEs or websockets is specified. +The reason is simplicity and openness to define additional mechanisms later. + +Optional [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) can be used with the bulk evaluation endpoint. +The motivation here is to save wire bandwidth and evaluation burden on the flag management system. +If present in response, the implementation will attach ETag when polling and respect 304 responses from the flag management system. + +Individual flag evaluations may be cached to increase performance. +But this is out of the scope of the OpenAPI specification and is an implementation detail of the provider. + +## Consequences + +- OFREP stays simple +- There is no way of implementing real-time flag updates for now + - Frequent evaluations from polling clients may introduce additional processing for feature flagging services +- Additional change detection mechanisms will need to be added later diff --git a/service/openapi.yaml b/service/openapi.yaml new file mode 100644 index 0000000..01c5568 --- /dev/null +++ b/service/openapi.yaml @@ -0,0 +1,276 @@ +openapi: 3.1.0 +info: + version: 0.1.0 + title: OpenFeature Remote Evaluation Protocol (OFREP) + description: OFREP define the protocol for remote flag evaluations + contact: + url: https://github.com/open-feature/protocol + license: + identifier: Apache-2.0 + name: Apache 2.0 +security: + - ApiKeyAuth: [ ] + - BearerAuth: [ ] +paths: + /ofrep/v1/evaluate/flags/{key}: + post: + description: OFREP single flag evaluation request + parameters: + - name: key + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/evaluationRequest' + responses: + '200': + description: OFREP successful evaluation response + content: + application/json: + schema: + $ref: '#/components/schemas/evaluationSuccess' + '400': + description: Bad evaluation request + content: + application/json: + schema: + $ref: '#/components/schemas/evaluationFailure' + '404': + description: Flag not found + content: + application/json: + schema: + $ref: '#/components/schemas/flagNotFound' + '401': + description: Unauthorized - You need credentials to access the API + '403': + description: Forbidden - You are not authorized to access the API + '429': + description: Rate limit reached on the Flag Management System + headers: + Retry-Later: + description: Indicates when to retry the request again + schema: + type: string + format: date-time + examples: + - '2024-02-07T12:00:00Z' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/generalErrorResponse' + /ofrep/v1/evaluate/flags: + post: + description: OFREP bulk evaluation request + 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 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/bulkEvaluationRequest' + responses: + '200': + description: OFREP successful evaluation response + headers: + ETag: + schema: + type: string + description: Entity tag used for cache validation + content: + application/json: + schema: + $ref: '#/components/schemas/bulkEvaluationSuccess' + '304': + description: Bulk evaluation is not modified + '400': + description: Bad evaluation request + content: + application/json: + schema: + $ref: '#/components/schemas/bulkEvaluationFailure' + '401': + description: Unauthorized - You need credentials to access the API + '403': + description: Forbidden - You are not authorized to access the API + '429': + description: Rate limit reached on the Flag Management System + headers: + Retry-Later: + description: Indicates when to retry the request again + schema: + type: string + format: date-time + examples: + - '2024-02-07T12:00:00Z' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/generalErrorResponse' +components: + securitySchemes: + BearerAuth: + description: (optional) Bearer Authorization to your flag management system. + type: http + scheme: bearer + ApiKeyAuth: + description: (optional) API Key to your flag management system. + type: apiKey + in: header + name: X-API-Key + schemas: + bulkEvaluationRequest: + description: Evaluate multiple flags in one request + properties: + context: + $ref: '#/components/schemas/context' + bulkEvaluationSuccess: + description: Success response for the bulk evaluation request + type: object + properties: + flags: + type: array + items: + oneOf: + - $ref: "#/components/schemas/evaluationSuccess" + - $ref: '#/components/schemas/evaluationFailure' + bulkEvaluationFailure: + description: Bulk evaluation failure response + properties: + errorCode: + type: string + description: An appropriate code specific to the bulk evaluation error. See https://openfeature.dev/specification/types#error-code + errorDetails: + type: string + description: Optional error details description for logging or other needs + required: + - errorCode + evaluationRequest: + description: Flag evaluation request + properties: + context: + $ref: '#/components/schemas/context' + evaluationSuccess: + description: Flag evaluation success response. + allOf: + - properties: + key: + $ref: "#/components/schemas/key" + reason: + type: string + enum: [ STATIC,TARGETING_MATCH,SPLIT,DISABLED,UNKNOWN ] + description: An OpenFeature reason for the evaluation + variant: + type: string + description: Variant of the evaluated flag value + metadata: + type: object + description: Arbitrary metadata supporting flag evaluation + - oneOf: + - $ref: "#/components/schemas/booleanFlag" + - $ref: "#/components/schemas/stringFlag" + - $ref: "#/components/schemas/integerFlag" + - $ref: "#/components/schemas/floatFlag" + - $ref: "#/components/schemas/objectFlag" + evaluationFailure: + description: Flag evaluation failure response + properties: + key: + $ref: '#/components/schemas/key' + errorCode: + type: string + enum: [ PARSE_ERROR,TARGETING_KEY_MISSING,INVALID_CONTEXT,GENERAL ] + description: OpenFeature compatible error code. See https://openfeature.dev/specification/types#error-code + errorDetails: + $ref: '#/components/schemas/errorDetails' + required: + - key + - errorCode + flagNotFound: + description: Flag not found response + properties: + key: + $ref: '#/components/schemas/key' + errorCode: + type: string + enum: [ FLAG_NOT_FOUND ] + errorDetails: + $ref: '#/components/schemas/errorDetails' + required: + - key + - errorCode + generalErrorResponse: + description: A general error response from the service + properties: + errorDetails: + $ref: '#/components/schemas/errorDetails' + key: + type: string + description: Feature flag key + examples: + - my-flag + context: + type: object + description: Context information for flag evaluation + booleanFlag: + description: A boolean typed flag value + properties: + value: + type: boolean + description: Flag evaluation result + required: + - value + stringFlag: + description: A string typed flag value + properties: + value: + type: string + description: Flag evaluation result + examples: + - "my-flag-value" + required: + - value + integerFlag: + description: An integer typed flag value + properties: + value: + type: integer + description: Flag evaluation result + examples: + - 3 + required: + - value + floatFlag: + description: A float typed flag value + properties: + value: + type: number + description: Flag evaluation result + examples: + - 3.1415 + required: + - value + objectFlag: + description: An object typed flag value + properties: + value: + type: object + description: Flag evaluation result + required: + - value + errorDetails: + type: string + description: An error description for logging or other needs