-
Notifications
You must be signed in to change notification settings - Fork 21
Feat node server side #18
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
base: main
Are you sure you want to change the base?
Changes from 65 commits
cbbd490
83de4ff
070aa7f
8840ac8
f829b2e
dc08564
6a27e82
66a1912
c50e1c2
a2fec41
a336452
ade1b65
d6ceca7
9fc56f8
174c81e
911e2db
316caa2
87221b9
dc77bb0
bcd5d99
cf61233
078fe8e
3f2819a
16e564f
7be093b
53f1bb8
f46c9cc
d0a85ae
b7baf02
39086ad
06340c9
8e5d3c0
3b4f24a
6f15e9b
ec228f5
69ae446
d9dd778
0191e54
02205d8
83a1bcd
d4fc5ea
498315f
3f400eb
6a4841c
dc44eb6
a5f7f94
1747955
489ed85
28e4633
9df8bcf
e7b14ca
0e24266
f604535
6f2c349
53b6a9a
b5bf94b
7b4d66b
619d24f
c784e8c
0f93096
2082531
85b55a0
c2c6c4d
f0f9438
c3cbd53
815d794
91fd26c
74cc9a3
860b35b
153e549
76c2a88
a6b24a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { ABBY_BASE_URL } from "./constants"; | ||
| import type { AbbyEventType, AbbyEvent, AbbyDataResponse } from "./index"; | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😍 |
||
| export abstract class HttpService { | ||
| static async getProjectData({ | ||
| projectId, | ||
|
|
@@ -21,6 +22,7 @@ export abstract class HttpService { | |
| const data = (await res.json()) as AbbyDataResponse; | ||
| return data; | ||
| } catch (err) { | ||
| console.log(err); | ||
| console.error("[ABBY]: failed to load project data, falling back to defaults"); | ||
| return null; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -121,8 +121,146 @@ describe("Abby", () => { | |||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBe(false); | ||||||
| }); | ||||||
|
|
||||||
| it("refetches an expired flag", async () =>{ | ||||||
| const date = new Date() //current date | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment says nothing, why dont we use
Suggested change
|
||||||
| vi.setSystemTime(date) | ||||||
| const abby = new Abby({ | ||||||
| projectId: "expired", | ||||||
| flags: ["flag1", "flag2"], | ||||||
| flagCacheConfig: { | ||||||
| refetchFlags: true, | ||||||
| timeToLive: 2 | ||||||
| } | ||||||
| }); | ||||||
| await abby.loadProjectData() | ||||||
| const expiredDate = new Date(new Date().getTime() + 1000 * 60 * 10) //date in 100 minutes | ||||||
| vi.setSystemTime(expiredDate) | ||||||
| const spy = vi.spyOn(abby, "refetchFlags") | ||||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).toBeCalled() | ||||||
| }) | ||||||
|
|
||||||
| it("non expired flag does not get refetched", async () => { | ||||||
| const date = new Date() //current date | ||||||
| vi.setSystemTime(date) | ||||||
| const abby = new Abby({ | ||||||
| projectId: "expired", | ||||||
| flags: ["flag1", "flag2"], | ||||||
| flagCacheConfig: { | ||||||
| refetchFlags: true, | ||||||
| timeToLive: 2 | ||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| await abby.loadProjectData(); | ||||||
|
|
||||||
| const spy = vi.spyOn(abby, "refetchFlags") | ||||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).not.toBeCalled() | ||||||
| }) | ||||||
|
|
||||||
| it("respects the featureFlagCacheConfig refetchFlags value set to false", async () => { | ||||||
| const date = new Date() //current date | ||||||
| vi.setSystemTime(date) | ||||||
| const abby = new Abby({ | ||||||
| projectId: "expired", | ||||||
| flags: ["flag1", "flag2"], | ||||||
| flagCacheConfig: { | ||||||
| refetchFlags: false, | ||||||
| timeToLive: 2 | ||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| await abby.loadProjectData(); | ||||||
|
|
||||||
| const spy = vi.spyOn(abby, "refetchFlags") | ||||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).not.toBeCalled() | ||||||
| }) | ||||||
|
|
||||||
| it("", async () => { | ||||||
| const date = new Date() //current date | ||||||
| vi.setSystemTime(date) | ||||||
| const abby = new Abby({ | ||||||
| projectId: "expired", | ||||||
| flags: ["flag1", "flag2"], | ||||||
| flagCacheConfig: { | ||||||
| refetchFlags: true, | ||||||
| timeToLive: 2 | ||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| await abby.loadProjectData(); | ||||||
|
|
||||||
| const spy = vi.spyOn(abby, "refetchFlags") | ||||||
|
|
||||||
| //set date to 5 Minutes in the future | ||||||
| const dateIn3Minutes = new Date(new Date().getTime() + 1000 * 60 * 5); | ||||||
| vi.setSystemTime(dateIn3Minutes) | ||||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).toBeCalled() | ||||||
| }) | ||||||
|
|
||||||
| it("respects the featureFlagCacheCOnfig expiration time", async () => { | ||||||
| const date = new Date() //current date | ||||||
| vi.setSystemTime(date) | ||||||
| const abby = new Abby({ | ||||||
| projectId: "expired", | ||||||
| flags: ["flag1", "flag2"], | ||||||
| flagCacheConfig: { | ||||||
| refetchFlags: true, | ||||||
| timeToLive: 2 | ||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| await abby.loadProjectData(); | ||||||
|
|
||||||
| const spy = vi.spyOn(abby, "refetchFlags") | ||||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).not.toBeCalled() | ||||||
|
|
||||||
| //set date to 5 Minutes in the future | ||||||
| const dateIn3Minutes = new Date(new Date().getTime() + 1000 * 60 * 5); | ||||||
| vi.setSystemTime(dateIn3Minutes) | ||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).toBeCalled() | ||||||
| }) | ||||||
|
|
||||||
| }); | ||||||
|
|
||||||
| it("respects the default behaviour", async () => { | ||||||
| const date = new Date() //current date | ||||||
| vi.setSystemTime(date) | ||||||
| const abby = new Abby({ | ||||||
| projectId: "expired", | ||||||
| flags: ["flag1", "flag2"], | ||||||
| }); | ||||||
|
|
||||||
| await abby.loadProjectData(); | ||||||
|
|
||||||
| const spy = vi.spyOn(abby, "refetchFlags") | ||||||
|
|
||||||
| //set date to 5 Minutes in the future | ||||||
| const dateIn3Minutes = new Date(new Date().getTime() + 1000 * 60 * 5); | ||||||
| vi.setSystemTime(dateIn3Minutes) | ||||||
|
|
||||||
| expect(abby.getFeatureFlag("flag1")).toBeTruthy(); | ||||||
| expect(abby.getFeatureFlag("flag2")).toBeFalsy(); | ||||||
| expect(spy).not.toBeCalled() | ||||||
| }) | ||||||
|
|
||||||
| describe("Math helpers", () => { | ||||||
| it("validates weight", () => { | ||||||
| const variants = ["variant1", "variant2"]; | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,5 +7,6 @@ export default defineConfig({ | |
| clean: true, | ||
| sourcemap: true, | ||
| treeshake: true, | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? |
||
| format: ["cjs", "esm"], | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| dist |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| { | ||
| "name": "@tryabby/nodejs", | ||
| "version": "1.0.0", | ||
| "description": "", | ||
| "scripts": { | ||
| "test": "vitest ", | ||
| "build": "tsup src/index.ts", | ||
| "dev": "tsnd --respawn --transpile-only ./src/index.ts", | ||
| "dev:fastify": "tsnd --respawn --transpile-only ./src/fastify/index.ts", | ||
| "dev:debugger": "nodemon ./src/index.ts --inspect" | ||
| }, | ||
| "main": "dist/index.js", | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "exports": { | ||
| ".": { | ||
| "import": "./dist/index.js", | ||
| "require": "./dist/index.cjs", | ||
| "types": "./dist/index.d.ts" | ||
| }, | ||
| "./express": { | ||
| "import": "./dist/express/abbyMiddlewareFactory.js", | ||
| "require": "./dist/express/abbyMiddlewareFactory.cjs", | ||
| "types": "./dist/abbyMiddlewareFactory.d.ts" | ||
| }, | ||
| "./node": { | ||
| "import": "./dist/abby/createAbby.js", | ||
| "require": "./dist/abby/createAbby.cjs", | ||
| "types": "./dist/createAbby.d.ts" | ||
| } | ||
| }, | ||
| "keywords": [], | ||
| "author": "", | ||
| "license": "ISC", | ||
| "dependencies": { | ||
| "@fastify/cookie": "^8.3.0", | ||
| "@tryabby/core": "workspace:^", | ||
| "express": "^4.18.2", | ||
| "fastify": "^4.19.2", | ||
| "ts-toolbelt": "^9.6.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/express": "^4.17.17", | ||
| "@types/jest": "^29.5.1", | ||
| "@types/node": "^20.2.5", | ||
| "@types/supertest": "^2.0.12", | ||
| "flush-promises": "^1.0.2", | ||
| "install": "^0.13.0", | ||
| "msw": "^0.49.1", | ||
| "node-fetch": "^3.3.1", | ||
| "nodemon": "^2.0.22", | ||
| "supertest": "^6.3.3", | ||
| "ts-node": "^10.9.1", | ||
| "ts-node-dev": "^2.0.0", | ||
| "tsconfig": "workspace:*", | ||
| "tsup": "^6.5.0", | ||
| "typescript": "^5.0.4", | ||
| "vite": "^4.3.9", | ||
| "vitest": "^0.31.4" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { createAbby } from "./createAbby"; | ||
|
|
||
| export const { getFeatureFlagValue, getABTestValue } = createAbby({ | ||
| projectId: "clfn3hs1t0002kx08x3kidi80", | ||
| currentEnvironment: process.env.NODE_ENV, | ||
| tests: { | ||
| "New Test3": { | ||
| variants: ["A", "B"], | ||
| }, | ||
| }, | ||
| flags: { | ||
| lol: "Boolean", | ||
| test3: "Boolean", | ||
| testAbby: "Boolean", | ||
| }, | ||
| flagCacheConfig: { | ||
| refetchFlags: true, | ||
| timeToLive: 1, | ||
| }, | ||
| }); | ||
|
|
||
| export const abby = { getFeatureFlagValue, getABTestValue }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // hacky way to get request object in storage service without passing it | ||
| import { Request } from "express"; | ||
| import { FastifyRequest } from "fastify"; | ||
|
|
||
| // type RequestType<T> = T extends Request ? Request : FastifyRequest; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? |
||
| type RequestType = Request | FastifyRequest | null; | ||
| let req: RequestType = null; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is this? there can be thousands of requests in parallel.. why is this a singleton? |
||
|
|
||
| export function setRequest(request: RequestType) { | ||
| req = request; | ||
| } | ||
|
|
||
| export function getRequest(): RequestType | null { | ||
| return req; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is done in another PR? maybe finish that one first? =)