Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added error strategy for node and browser clients #745

Merged
merged 2 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions flipt-client-browser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 provide, the client will be default to fail. See the [Error Strategies](#error-strategies) section for more information.

### Authentication

Expand All @@ -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.
Expand Down
35 changes: 23 additions & 12 deletions flipt-client-browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
EvaluationResponse,
Flag,
IFetcher,
VariantEvaluationResponse
VariantEvaluationResponse,
ErrorStrategy
} from './models';

import {
Expand All @@ -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;
Expand All @@ -40,7 +42,8 @@ export class FliptEvaluationClient {
namespace: string = 'default',
options: ClientOptions = {
url: 'http://localhost:8080',
reference: ''
reference: '',
errorStrategy: ErrorStrategy.Fail
}
): Promise<FliptEvaluationClient> {
await init(await wasm());
Expand Down Expand Up @@ -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;
}

Expand All @@ -122,19 +126,26 @@ export class FliptEvaluationClient {
* @returns true if snapshot changed
*/
public async refresh(): Promise<boolean> {
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;
}

/**
Expand Down
15 changes: 15 additions & 0 deletions flipt-client-browser/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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'
}
8 changes: 8 additions & 0 deletions flipt-client-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 provide, the client will be default to fail. See the [Error Strategies](#error- strategies) section for more information.

### Authentication

Expand All @@ -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.
Expand Down
36 changes: 23 additions & 13 deletions flipt-client-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
BatchEvaluationResponse,
ErrorEvaluationResponse,
EvaluationResponse,
Flag
Flag,
ErrorStrategy
} from './models';

import {
Expand All @@ -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;
Expand All @@ -43,7 +45,8 @@ export class FliptEvaluationClient {
options: ClientOptions<AuthenticationStrategy> = {
url: 'http://localhost:8080',
reference: '',
updateInterval: 120
updateInterval: 120,
errorStrategy: ErrorStrategy.Fail
}
): Promise<FliptEvaluationClient> {
let url = options.url ?? 'http://localhost:8080';
Expand Down Expand Up @@ -111,7 +114,7 @@ export class FliptEvaluationClient {
if (options.updateInterval && options.updateInterval > 0) {
client.startAutoRefresh(options.updateInterval * 1000);
}

client.errorStrategy = options.errorStrategy;
return client;
}

Expand Down Expand Up @@ -153,19 +156,26 @@ export class FliptEvaluationClient {
* @returns {boolean} true if snapshot changed
*/
public async refresh(): Promise<boolean> {
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;
}

/**
Expand Down
15 changes: 15 additions & 0 deletions flipt-client-node/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ export interface ClientOptions<T> {
*/
reference?: string;
fetcher?: IFetcher;

/**
* The strategy to use for errors during refresh snapshot calls. {@link ErrorStrategy}
*/
errorStrategy?: ErrorStrategy;
}

/**
Expand Down Expand Up @@ -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'
}
Loading