Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
55 changes: 53 additions & 2 deletions packages/node-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,43 @@ bucketClient.initialize().then(() => {

![Config type check failed](docs/type-check-payload-failed.png "Remote config type check failed")

## Testing

When writing tests that cover code with feature flags, you can toggle features on/off programmatically to test the different behavior.

`bucket.ts`:

```typescript
import { BucketClient } from "@bucketco/node-sdk";

export const bucket = new BucketClient();
```

`app.test.ts`:

```typescript
import { bucket } from "./bucket.ts";

beforeAll(async () => await bucket.initialize());
afterEach(() => {
bucket.clearFeatureOverrides();
});

describe("API Tests", () => {
it("should return 200 for the root endpoint", async () => {
bucket.featureOverrides = {
"show-todo": true,
};

const response = await request(app).get("/");
expect(response.status).toBe(200);
expect(response.body).toEqual({ message: "Ready to manage some TODOs!" });
});
});
```

See more on feature overrides in the section below.

## Feature Overrides

Feature overrides allow you to override feature flags and their configurations locally. This is particularly useful for development and testing. You can specify overrides in three ways:
Expand All @@ -414,7 +451,7 @@ BUCKET_FEATURES_ENABLED=feature1,feature2
BUCKET_FEATURES_DISABLED=feature3,feature4
```

1. Through `bucketConfig.json`:
2. Through `bucketConfig.json`:

```json
{
Expand All @@ -433,7 +470,21 @@ BUCKET_FEATURES_DISABLED=feature3,feature4
}
```

1. Programmatically through the client options:
3. Programmatically through the client options:

You can use a simple `Record<string, boolean>` and pass it either in the constructor or by setting `client.featureOverrides`:

```typescript
// pass directly in the constructor
const client = new BucketClient({ featureOverrides: { myFeature: true } });
// or set on the client at a later time
client.featureOverrides = { myFeature: false };

// clear feature overrides. Same as setting to {}.
client.clearFeatureOverrides();
```

To get dynamic overrides, use a function which takes a context and returns a boolean or an object with the shape of `{isEnabled, config}`:

```typescript
import { BucketClient, Context } from "@bucketco/node-sdk";
Expand Down
4 changes: 2 additions & 2 deletions packages/node-sdk/example/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import bucket from "./bucket";

beforeAll(async () => await bucket.initialize());
beforeEach(() => {
bucket.featureOverrides = () => ({
bucket.featureOverrides = {
"show-todos": true,
});
};
});

describe("API Tests", () => {
Expand Down
50 changes: 43 additions & 7 deletions packages/node-sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
EvaluatedFeaturesAPIResponse,
FeatureAPIResponse,
FeatureDefinition,
FeatureOverrides,
FeatureOverridesFn,
IdType,
RawFeature,
Expand Down Expand Up @@ -338,9 +339,38 @@ export class BucketClient {
* @remarks
* The feature overrides are used to override the feature definitions.
* This is useful for testing or development.
*
* @example
* ```ts
* client.featureOverrides = {
* "feature-1": true,
* "feature-2": false,
* };
* ```
**/
set featureOverrides(overrides: FeatureOverridesFn | FeatureOverrides) {
if (typeof overrides === "object") {
this._config.featureOverrides = () => overrides;
} else {
this._config.featureOverrides = overrides;
}
}

/**
* Clears the feature overrides.
*
* @remarks
* This is useful for testing or development.
*
* @example
* ```ts
* afterAll(() => {
* client.clearFeatureOverrides();
* });
* ```
**/
set featureOverrides(overrides: FeatureOverridesFn) {
this._config.featureOverrides = overrides;
clearFeatureOverrides() {
this._config.featureOverrides = () => ({});
}

/**
Expand Down Expand Up @@ -1107,11 +1137,17 @@ export class BucketClient {
this._config.featureOverrides(context),
).map(([key, override]) => [
key,
{
key,
isEnabled: isObject(override) ? override.isEnabled : !!override,
config: isObject(override) ? override.config : undefined,
},
isObject(override)
? {
key,
isEnabled: override.isEnabled,
config: override.config,
}
: {
key,
isEnabled: !!override,
config: undefined,
},
]);

if (overrides.length > 0) {
Expand Down
Loading