diff --git a/flipt-client-browser/README.md b/flipt-client-browser/README.md index cecb5136..ef9607da 100644 --- a/flipt-client-browser/README.md +++ b/flipt-client-browser/README.md @@ -50,6 +50,7 @@ The `FliptEvaluationClient` constructor accepts two optional arguments: - `authentication`: The authentication strategy to use when communicating with the upstream Flipt instance. If not provided, the client will default to no authentication. See the [Authentication](#authentication) section for more information. - `reference`: The [reference](https://docs.flipt.io/guides/user/using-references) to use when fetching flag state. If not provided, reference will not be used. - `fetcher`: An implementation of a [fetcher](https://github.com/flipt-io/flipt-client-sdks/blob/4821cb227c6c8b10419b96674d44ad1d6668a647/flipt-client-browser/src/models.ts#L5) interface to use when requesting flag state. If not provided, a default fetcher using the browser's [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) will be used. + - `errorStrategy`: The error strategy to use when fetching flag state. If not provided, the client will default to fail. See the [Error Strategies](#error-strategies) section for more information. ### Authentication @@ -59,6 +60,13 @@ The `FliptEvaluationClient` supports the following authentication strategies: - [Client Token Authentication](https://docs.flipt.io/authentication/using-tokens) - [JWT Authentication](https://docs.flipt.io/authentication/using-jwts) +### Error Strategies + +The client `errorStrategy` option supports the following error strategies: + +- `fail`: The client will throw an error if the flag state cannot be fetched. This is the default behavior. +- `fallback`: The client will maintain the last known good state and use that state for evaluation in case of an error. + ### Custom Fetcher The `FliptEvaluationClient` supports custom fetchers. This allows you to fetch flag state from a custom source or override HTTP headers. diff --git a/flipt-client-browser/src/index.ts b/flipt-client-browser/src/index.ts index 45536a6c..9e2031a4 100644 --- a/flipt-client-browser/src/index.ts +++ b/flipt-client-browser/src/index.ts @@ -10,7 +10,8 @@ import { EvaluationResponse, Flag, IFetcher, - VariantEvaluationResponse + VariantEvaluationResponse, + ErrorStrategy } from './models'; import { @@ -24,6 +25,7 @@ export class FliptEvaluationClient { private engine: Engine; private fetcher: IFetcher; private etag?: string; + private errorStrategy?: ErrorStrategy; private constructor(engine: Engine, fetcher: IFetcher) { this.engine = engine; @@ -40,7 +42,8 @@ export class FliptEvaluationClient { namespace: string = 'default', options: ClientOptions = { url: 'http://localhost:8080', - reference: '' + reference: '', + errorStrategy: ErrorStrategy.Fail } ): Promise { await init(await wasm()); @@ -104,6 +107,7 @@ export class FliptEvaluationClient { engine.snapshot(data); const client = new FliptEvaluationClient(engine, fetcher); client.storeEtag(resp); + client.errorStrategy = options.errorStrategy; return client; } @@ -122,19 +126,26 @@ export class FliptEvaluationClient { * @returns true if snapshot changed */ public async refresh(): Promise { - const opts = { etag: this.etag }; - const resp = await this.fetcher(opts); + try { + const opts = { etag: this.etag }; + const resp = await this.fetcher(opts); - let etag = resp.headers.get('etag'); - if (this.etag && this.etag === etag) { - return false; - } + let etag = resp.headers.get('etag'); + if (this.etag && this.etag === etag) { + return false; + } - this.storeEtag(resp); + this.storeEtag(resp); - const data = await resp.json(); - this.engine.snapshot(data); - return true; + const data = await resp.json(); + this.engine.snapshot(data); + return true; + } catch (error) { + if (this.errorStrategy === ErrorStrategy.Fail) { + throw error; // Re-throw the error + } + } + return false; } /** diff --git a/flipt-client-browser/src/models.ts b/flipt-client-browser/src/models.ts index 6897fdc0..7740f05a 100644 --- a/flipt-client-browser/src/models.ts +++ b/flipt-client-browser/src/models.ts @@ -67,6 +67,11 @@ export interface ClientOptions { */ reference?: string; fetcher?: IFetcher; + + /** + * The strategy to use for errors during refresh snapshot calls. {@link ErrorStrategy} + */ + errorStrategy?: ErrorStrategy; } /** @@ -191,3 +196,13 @@ export interface BatchEvaluationResponse { /** Duration of the request in milliseconds. */ requestDurationMillis: number; } + +/** + * Defines the strategy to handle errors during the flags snapshot update calls. + */ +export enum ErrorStrategy { + /** The client will throw an error if the flag state cannot be fetched. This is the default behavior. */ + Fail = 'fail', + /** The client will maintain the last known good state and use that state for evaluation in case of an error. */ + Fallback = 'fallback' +} diff --git a/flipt-client-node/README.md b/flipt-client-node/README.md index 5183739b..3667abe8 100644 --- a/flipt-client-node/README.md +++ b/flipt-client-node/README.md @@ -53,6 +53,7 @@ The `FliptEvaluationClient` constructor accepts two optional arguments: - `updateInterval`: The interval (in seconds) in which to fetch new flag state. If not provided, the client will default to 120 seconds. - `authentication`: The authentication strategy to use when communicating with the upstream Flipt instance. If not provided, the client will default to no authentication. See the [Authentication](#authentication) section for more information. - `reference`: The [reference](https://docs.flipt.io/guides/user/using-references) to use when fetching flag state. If not provided, reference will not be used. + - `errorStrategy`: The error strategy to use when fetching flag state. If not provided, the client will default to fail. See the [Error Strategies](#error- strategies) section for more information. ### Authentication @@ -62,6 +63,13 @@ The `FliptEvaluationClient` supports the following authentication strategies: - [Client Token Authentication](https://docs.flipt.io/authentication/using-tokens) - [JWT Authentication](https://docs.flipt.io/authentication/using-jwts) +### Error Strategies + +The client `errorStrategy` option supports the following error strategies: + +- `fail`: The client will throw an error if the flag state cannot be fetched. This is the default behavior. +- `fallback`: The client will maintain the last known good state and use that state for evaluation in case of an error. + ### Custom Fetcher The `FliptEvaluationClient` supports custom fetchers. This allows you to fetch flag state from a custom source or override HTTP headers. diff --git a/flipt-client-node/src/index.ts b/flipt-client-node/src/index.ts index bec4496b..23c0edd0 100644 --- a/flipt-client-node/src/index.ts +++ b/flipt-client-node/src/index.ts @@ -11,7 +11,8 @@ import { BatchEvaluationResponse, ErrorEvaluationResponse, EvaluationResponse, - Flag + Flag, + ErrorStrategy } from './models'; import { @@ -26,6 +27,7 @@ export class FliptEvaluationClient { private fetcher: IFetcher; private etag?: string; private updateInterval?: NodeJS.Timeout; + private errorStrategy?: ErrorStrategy; private constructor(engine: Engine, fetcher: IFetcher) { this.engine = engine; @@ -43,7 +45,8 @@ export class FliptEvaluationClient { options: ClientOptions = { url: 'http://localhost:8080', reference: '', - updateInterval: 120 + updateInterval: 120, + errorStrategy: ErrorStrategy.Fail } ): Promise { let url = options.url ?? 'http://localhost:8080'; @@ -111,7 +114,7 @@ export class FliptEvaluationClient { if (options.updateInterval && options.updateInterval > 0) { client.startAutoRefresh(options.updateInterval * 1000); } - + client.errorStrategy = options.errorStrategy; return client; } @@ -153,19 +156,26 @@ export class FliptEvaluationClient { * @returns {boolean} true if snapshot changed */ public async refresh(): Promise { - const opts = { etag: this.etag }; - const resp = await this.fetcher(opts); + try { + const opts = { etag: this.etag }; + const resp = await this.fetcher(opts); - let etag = resp.headers.get('etag'); - if (this.etag && this.etag === etag) { - return false; - } + let etag = resp.headers.get('etag'); + if (this.etag && this.etag === etag) { + return false; + } - this.storeEtag(resp); + this.storeEtag(resp); - const data = await resp.json(); - this.engine.snapshot(data); - return true; + const data = await resp.json(); + this.engine.snapshot(data); + return true; + } catch (err) { + if (this.errorStrategy === ErrorStrategy.Fail) { + throw err; // Re-throw the error + } + } + return false; } /** diff --git a/flipt-client-node/src/models.ts b/flipt-client-node/src/models.ts index 30281b4a..15d31863 100644 --- a/flipt-client-node/src/models.ts +++ b/flipt-client-node/src/models.ts @@ -67,6 +67,11 @@ export interface ClientOptions { */ reference?: string; fetcher?: IFetcher; + + /** + * The strategy to use for errors during refresh snapshot calls. {@link ErrorStrategy} + */ + errorStrategy?: ErrorStrategy; } /** @@ -191,3 +196,13 @@ export interface BatchEvaluationResponse { /** Duration of the request in milliseconds. */ requestDurationMillis: number; } + +/** + * Defines the strategy to handle errors during the flags snapshot update calls. + */ +export enum ErrorStrategy { + /** The client will throw an error if the flag state cannot be fetched. This is the default behavior. */ + Fail = 'fail', + /** The client will maintain the last known good state and use that state for evaluation in case of an error. */ + Fallback = 'fallback' +}