Skip to content

Commit 219eddd

Browse files
authored
feat: add flagsmith-node-js provider (#1374)
1 parent 0e0b966 commit 219eddd

19 files changed

+12857
-28
lines changed

.release-please-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@
2121
"libs/shared/config-cat-core": "0.2.2",
2222
"libs/providers/unleash-web": "0.1.1",
2323
"libs/providers/growthbook": "0.1.2",
24-
"libs/providers/aws-ssm": "0.1.3"
24+
"libs/providers/aws-ssm": "0.1.3",
25+
"libs/providers/flagsmith-provider": "0.1.0"
2526
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"extends": ["../../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*", "node_modules/**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["*.ts", "*.tsx"],
11+
"rules": {}
12+
},
13+
{
14+
"files": ["*.js", "*.jsx"],
15+
"rules": {}
16+
},
17+
{
18+
"files": ["*.json"],
19+
"rules": {
20+
"@nx/dependency-checks": [
21+
"error",
22+
{
23+
"ignoredDependencies": ["flagsmith-nodejs"]
24+
}
25+
]
26+
}
27+
}
28+
]
29+
}

libs/providers/flagsmith/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Flagsmith Provider
2+
3+
This is an OpenFeature provider implementation for using [Flagsmith](https://flagsmith.com), a managed feature flag and remote config platform for Node.js applications.
4+
5+
## Installation
6+
7+
```bash
8+
npm install @openfeature/flagsmith-provider @openfeature/server-sdk@^1.19 flagsmith-nodejs@^6.1
9+
```
10+
11+
## Usage
12+
13+
The Flagsmith provider uses the [Flagsmith Node.js SDK](https://docs.flagsmith.com/clients/server-side).
14+
15+
It can be created by passing a configured Flagsmith client instance to the `FlagsmithOpenFeatureProvider` constructor.
16+
17+
### Example using the default configuration
18+
19+
```javascript
20+
import { OpenFeature } from '@openfeature/server-sdk';
21+
import FlagsmithOpenFeatureProvider from '@openfeature/flagsmith-provider';
22+
import Flagsmith from 'flagsmith-nodejs';
23+
24+
// Create the Flagsmith client
25+
const flagsmith = new Flagsmith({
26+
environmentKey: '<your_environment_key>',
27+
});
28+
29+
// Create and set the provider
30+
const provider = new FlagsmithOpenFeatureProvider(flagsmith);
31+
await OpenFeature.setProviderAndWait(provider);
32+
33+
// Obtain a client instance and evaluate feature flags
34+
const client = OpenFeature.getClient();
35+
36+
const value = await client.getBooleanValue('my-flag', false, { targetingKey: 'user-123' });
37+
console.log(`my-flag: ${value}`);
38+
39+
// On application shutdown, clean up the OpenFeature provider
40+
await OpenFeature.clearProviders();
41+
```
42+
43+
### Example using custom configuration
44+
45+
```javascript
46+
import { OpenFeature } from '@openfeature/server-sdk';
47+
import FlagsmithOpenFeatureProvider from '@openfeature/flagsmith-provider';
48+
import Flagsmith from 'flagsmith-nodejs';
49+
50+
// Create the Flagsmith client with custom options
51+
const flagsmith = new Flagsmith({
52+
environmentKey: '<your_environment_key>',
53+
enableLocalEvaluation: true,
54+
retries: 3,
55+
});
56+
57+
// Create the provider with custom configuration
58+
const provider = new FlagsmithOpenFeatureProvider(flagsmith, {
59+
returnValueForDisabledFlags: true,
60+
useFlagsmithDefaults: true,
61+
useBooleanConfigValue: false,
62+
});
63+
64+
await OpenFeature.setProviderAndWait(provider);
65+
66+
// ...
67+
```
68+
69+
## Configuration Options
70+
71+
The provider accepts the following configuration options:
72+
73+
| Option | Type | Default | Description |
74+
| ----------------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
75+
| `returnValueForDisabledFlags` | `boolean` | `false` | If `true`, returns flag values even when disabled. If `false`, throws error for disabled flags (except boolean flags which return `false` with reason `DISABLED` when `useBooleanConfigValue=false`) |
76+
| `useFlagsmithDefaults` | `boolean` | `false` | If `true`, allows using Flagsmith default flag values. If `false`, returns default value with error code for missing flags |
77+
| `useBooleanConfigValue` | `boolean` | `false` | If `true`, returns `flag.value` for boolean flags. If `false`, returns `flag.enabled` |
78+
79+
## Evaluation Context
80+
81+
The OpenFeature Evaluation Context is mapped to Flagsmith's identity and traits system.
82+
83+
### Identity Resolution
84+
85+
- If `targetingKey` is provided in the evaluation context, the provider will use `getIdentityFlags()` to retrieve flags for that specific identity
86+
- If no `targetingKey` is provided, the provider will use `getEnvironmentFlags()` to retrieve environment-level flags
87+
88+
### Traits
89+
90+
The `traits` field in the evaluation context is passed directly to Flagsmith as user traits for targeting and segmentation.
91+
92+
#### Example
93+
94+
```javascript
95+
const evaluationContext = {
96+
targetingKey: 'user-123',
97+
traits: {
98+
99+
plan: 'premium',
100+
age: 25,
101+
},
102+
};
103+
104+
const value = await client.getBooleanValue('premium-feature', false, evaluationContext);
105+
```
106+
107+
## Flag Value Types
108+
109+
The provider supports all OpenFeature flag value types:
110+
111+
- **Boolean**: Returns `flag.enabled` by default, or `flag.value` if `useBooleanConfigValue` is true
112+
- **String**: Returns the flag value as-is if it's a string
113+
- **Number**: Attempts to parse the flag value as a number
114+
- **Object**: Attempts to parse the flag value as JSON
115+
116+
## Error Handling
117+
118+
The provider handles various error scenarios:
119+
120+
- **Flag Not Found**: Returns default value with `FLAG_NOT_FOUND` error code
121+
- **Type Mismatch**: Returns default value with `TYPE_MISMATCH` error code if flag value cannot be converted to requested type
122+
- **Disabled Flags**:
123+
– For boolean flags with `useBooleanConfigValue=false`: returns `false` with reason `DISABLED`
124+
– For other flags: throws `GeneralError` unless `returnValueForDisabledFlags` is `true`
125+
- **General Errors**: Throws `GeneralError` for client communication issues
126+
127+
## Building
128+
129+
Run:
130+
131+
```bash
132+
nx package providers-flagsmith
133+
```
134+
135+
to build the library.
136+
137+
## Running unit tests
138+
139+
Run:
140+
141+
```bash
142+
npx nx run providers-flagsmith:test
143+
```
144+
145+
to execute the unit tests via [Jest](https://jestjs.io).
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* eslint-disable */
2+
export default {
3+
displayName: 'providers-flagsmith',
4+
preset: '../../../jest.preset.js',
5+
transform: {
6+
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7+
},
8+
moduleFileExtensions: ['ts', 'js', 'html'],
9+
coverageDirectory: '../../../coverage/libs/providers/flagsmith',
10+
};

0 commit comments

Comments
 (0)