Skip to content

Commit bb17e3f

Browse files
committed
feat: multi provider implementation for Node
1 parent c2adbfb commit bb17e3f

24 files changed

+6249
-2274
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
"libs/providers/flipt": "0.1.0",
1515
"libs/providers/flagsmith-client": "0.1.2",
1616
"libs/providers/flipt-web": "0.1.0",
17+
"libs/providers/multi-provider": "0.1.0",
1718
"libs/providers/growthbook-client": "0.1.1"
1819
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"extends": ["../../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
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+
"parser": "jsonc-eslint-parser",
20+
"rules": {
21+
"@nx/dependency-checks": "error"
22+
}
23+
}
24+
]
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Multi Provider
2+
3+
## Installation
4+
5+
```
6+
$ npm install @openfeature/multi-provider
7+
```
8+
9+
## Building
10+
11+
Run `nx package providers-multi-provider` to build the library.
12+
13+
## Running unit tests
14+
15+
Run `nx test providers-multi-provider` to execute the unit tests via [Jest](https://jestjs.io).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": [["minify", { "builtIns": false }]]
3+
}
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-multi-provider',
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/multi-provider',
10+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "@openfeature/vendor-migration-provider",
3+
"version": "0.0.1",
4+
"dependencies": {
5+
"tslib": "^2.3.0"
6+
},
7+
"main": "./src/index.js",
8+
"typings": "./src/index.d.ts",
9+
"scripts": {
10+
"publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi",
11+
"current-version": "echo $npm_package_version"
12+
},
13+
"peerDependencies": {
14+
"@openfeature/server-sdk": "^1.6.0"
15+
}
16+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"name": "providers-multi-provider",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/providers/multi-provider/src",
5+
"projectType": "library",
6+
"targets": {
7+
"publish": {
8+
"executor": "nx:run-commands",
9+
"options": {
10+
"command": "npm run publish-if-not-exists",
11+
"cwd": "dist/libs/providers/multi"
12+
},
13+
"dependsOn": [
14+
{
15+
"projects": "self",
16+
"target": "package"
17+
}
18+
]
19+
},
20+
"lint": {
21+
"executor": "@nx/linter:eslint",
22+
"outputs": ["{options.outputFile}"],
23+
"options": {
24+
"lintFilePatterns": ["libs/providers/multi-provider/**/*.ts", "libs/providers/multi-provider/package.json"]
25+
}
26+
},
27+
"test": {
28+
"executor": "@nx/jest:jest",
29+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
30+
"options": {
31+
"jestConfig": "libs/providers/multi-provider/jest.config.ts",
32+
"passWithNoTests": true
33+
},
34+
"configurations": {
35+
"ci": {
36+
"ci": true,
37+
"codeCoverage": true
38+
}
39+
}
40+
},
41+
"package": {
42+
"executor": "@nx/rollup:rollup",
43+
"outputs": ["{options.outputPath}"],
44+
"options": {
45+
"project": "libs/providers/multi-provider/package.json",
46+
"outputPath": "dist/libs/providers/multi",
47+
"entryFile": "libs/providers/multi-provider/src/index.ts",
48+
"tsConfig": "libs/providers/multi-provider/tsconfig.lib.json",
49+
"buildableProjectDepsInPackageJsonType": "dependencies",
50+
"compiler": "tsc",
51+
"generateExportsField": true,
52+
"umdName": "multi",
53+
"external": "all",
54+
"format": ["cjs", "esm"],
55+
"assets": [
56+
{
57+
"glob": "package.json",
58+
"input": "./assets",
59+
"output": "./src/"
60+
},
61+
{
62+
"glob": "LICENSE",
63+
"input": "./",
64+
"output": "./"
65+
},
66+
{
67+
"glob": "README.md",
68+
"input": "./libs/providers/multi",
69+
"output": "./"
70+
}
71+
]
72+
}
73+
}
74+
},
75+
"tags": []
76+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './lib/multi-provider';
2+
export * from './lib/errors';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ErrorCode, GeneralError, OpenFeatureError } from '@openfeature/server-sdk';
2+
3+
export class ErrorWithCode extends OpenFeatureError {
4+
constructor(
5+
public code: ErrorCode,
6+
message: string,
7+
) {
8+
super(message);
9+
}
10+
}
11+
12+
export class AggregateError extends GeneralError {
13+
constructor(
14+
message: string,
15+
public originalErrors: { source: string; error: unknown }[],
16+
) {
17+
super(message);
18+
}
19+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { EvaluationDetails, FlagValue, Hook, HookContext, HookHints, Logger } from '@openfeature/server-sdk';
2+
3+
/**
4+
* Utility for executing a set of hooks of each type. Implementation is largely copied from the main OpenFeature SDK.
5+
*/
6+
export class HookExecutor {
7+
constructor(private logger: Logger) {}
8+
9+
async beforeHooks(hooks: Hook[] | undefined, hookContext: HookContext, hints: HookHints) {
10+
const newContext = { ...hookContext.context };
11+
for (const hook of hooks ?? []) {
12+
// freeze the hookContext
13+
Object.freeze(hookContext);
14+
15+
Object.assign(newContext, {
16+
...(await hook?.before?.(hookContext, Object.freeze(hints))),
17+
});
18+
}
19+
20+
// after before hooks, freeze the EvaluationContext.
21+
return Object.freeze(newContext);
22+
}
23+
24+
async afterHooks(
25+
hooks: Hook[] | undefined,
26+
hookContext: HookContext,
27+
evaluationDetails: EvaluationDetails<FlagValue>,
28+
hints: HookHints,
29+
) {
30+
// run "after" hooks sequentially
31+
for (const hook of hooks ?? []) {
32+
await hook?.after?.(hookContext, evaluationDetails, hints);
33+
}
34+
}
35+
36+
async errorHooks(hooks: Hook[] | undefined, hookContext: HookContext, err: unknown, hints: HookHints) {
37+
// run "error" hooks sequentially
38+
for (const hook of hooks ?? []) {
39+
try {
40+
await hook?.error?.(hookContext, err, hints);
41+
} catch (err) {
42+
this.logger.error(`Unhandled error during 'error' hook: ${err}`);
43+
if (err instanceof Error) {
44+
this.logger.error(err.stack);
45+
}
46+
this.logger.error((err as Error)?.stack);
47+
}
48+
}
49+
}
50+
51+
async finallyHooks(hooks: Hook[] | undefined, hookContext: HookContext, hints: HookHints) {
52+
// run "finally" hooks sequentially
53+
for (const hook of hooks ?? []) {
54+
try {
55+
await hook?.finally?.(hookContext, hints);
56+
} catch (err) {
57+
this.logger.error(`Unhandled error during 'finally' hook: ${err}`);
58+
if (err instanceof Error) {
59+
this.logger.error(err.stack);
60+
}
61+
this.logger.error((err as Error)?.stack);
62+
}
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)