-
Notifications
You must be signed in to change notification settings - Fork 21
Feat(core): add feature flag caching #21
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 10 commits
8df2e44
d73a0ad
2f7f0e3
55627e6
443cc08
c3e075b
1e0d542
0033e0d
5de80ab
008d37d
2ef57e4
7d72fe5
013f01b
e086c29
6f354e6
c37e887
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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -43,6 +43,11 @@ interface PersistentStorage { | |||||
| set: (key: string, value: string) => void; | ||||||
| } | ||||||
|
|
||||||
| type flagCacheConfig = { | ||||||
| refetchFlags: boolean, | ||||||
|
Tim-53 marked this conversation as resolved.
Outdated
|
||||||
| timeToLive: number | ||||||
|
Tim-53 marked this conversation as resolved.
Outdated
|
||||||
| } | ||||||
|
|
||||||
| export type AbbyConfig< | ||||||
| FlagName extends string = string, | ||||||
| Tests extends Record<string, ABConfig> = Record<string, ABConfig> | ||||||
|
|
@@ -54,6 +59,7 @@ export type AbbyConfig< | |||||
| flags?: Array<FlagName>; | ||||||
| settings?: Settings<F.NoInfer<FlagName>>; | ||||||
| debug?: boolean; | ||||||
| flagCacheConfig?: flagCacheConfig, | ||||||
| }; | ||||||
|
|
||||||
| export class Abby< | ||||||
|
|
@@ -62,7 +68,7 @@ export class Abby< | |||||
| Tests extends Record<string, ABConfig> | ||||||
| > { | ||||||
| private log = (...args: any[]) => | ||||||
| this.config.debug ? console.log(`core.Abby`, ...args) : () => {}; | ||||||
| this.config.debug ? console.log(`core.Abby`, ...args) : () => { }; | ||||||
|
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. do we use
Collaborator
Author
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. We dont have a proper logger integrated right now , so is this used to log info in debugging mode atm. |
||||||
|
|
||||||
| private testDevtoolOverrides: Map< | ||||||
| keyof Tests, | ||||||
|
|
@@ -71,6 +77,8 @@ export class Abby< | |||||
|
|
||||||
| private flagDevtoolOverrides: Map<FlagName, boolean> = new Map(); | ||||||
|
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. guess this shouldn't be "just booleans" anymore, right? |
||||||
|
|
||||||
| #flagTimeoutMap:Map<string, Date> = new Map; | ||||||
|
Tim-53 marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| #data: LocalData<FlagName, TestName> = { | ||||||
| tests: {} as any, | ||||||
| flags: {} as any, | ||||||
|
|
@@ -151,6 +159,8 @@ export class Abby< | |||||
| return acc; | ||||||
| }, (this.config.tests ?? {}) as any), | ||||||
| flags: data.flags.reduce((acc, { name, isEnabled }) => { | ||||||
| const validUntil = new Date(new Date().getTime() + 1000 * 60 *( this.config.flagCacheConfig?.timeToLive ?? 1)); // flagdefault timeout is 1 minute | ||||||
|
Tim-53 marked this conversation as resolved.
Outdated
|
||||||
| this.#flagTimeoutMap.set(name, validUntil) | ||||||
| acc[name] = isEnabled; | ||||||
| return acc; | ||||||
| }, {} as Record<string, boolean>), | ||||||
|
|
@@ -203,6 +213,37 @@ export class Abby< | |||||
| return this.getProjectData(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Helper function to retrieve the time a flag is valid | ||||||
| * @param key | ||||||
| * @returns | ||||||
| */ | ||||||
| getFeatureFlagTimeout<F extends FlagName>(key: F) { | ||||||
| return this.#flagTimeoutMap.get(key) | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Helper function to check if a featureflag should be refetched | ||||||
| * @param key name of the featureflag | ||||||
| * @returns value of flag | ||||||
| */ | ||||||
| getValidFlag<F extends FlagName>(key: F) { | ||||||
| const flagTime = this.#flagTimeoutMap.get(key) | ||||||
| if (!flagTime) return this.#data.flags[key]; | ||||||
| const now = new Date(); | ||||||
| if (flagTime.getTime() <= now.getTime()) { | ||||||
|
Tim-53 marked this conversation as resolved.
Outdated
|
||||||
| this.refetchFlags() | ||||||
|
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. this is async.. so without waiting the return down below may be old though 🤷 |
||||||
| } | ||||||
| return this.#data.flags[key]; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * helper function to make testing easier | ||||||
| */ | ||||||
| refetchFlags() { | ||||||
| this.loadProjectData(); | ||||||
|
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.
Suggested change
Collaborator
Author
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. loadProjectData() has return type void. |
||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Function to get the value of a feature flag. This includes | ||||||
| * the overrides from the dev tools and the local overrides if in development mode | ||||||
|
|
@@ -213,8 +254,6 @@ export class Abby< | |||||
| getFeatureFlag<T extends FlagName>(key: T) { | ||||||
| this.log(`getFeatureFlag()`, key); | ||||||
|
|
||||||
| const storedValue = this.#data.flags[key]; | ||||||
|
|
||||||
| const localOverride = this.flagOverrides?.get(key); | ||||||
|
|
||||||
| if (localOverride != null) { | ||||||
|
|
@@ -227,7 +266,7 @@ export class Abby< | |||||
| * 1. DevTools | ||||||
| * 2. DevOverrides from config | ||||||
| * 3. DevDefault from config | ||||||
| */ | ||||||
| */ | ||||||
| if (process.env.NODE_ENV === "development") { | ||||||
| const devOverride = (this.config.settings?.flags?.devOverrides as any)?.[ | ||||||
| key | ||||||
|
|
@@ -240,6 +279,8 @@ export class Abby< | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| const storedValue = this.config.flagCacheConfig?.refetchFlags ? this.getValidFlag(key) : this.#data.flags[key]; | ||||||
|
Tim-53 marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| if (storedValue != null) { | ||||||
| this.log(`getFeatureFlag() => storedValue:`, storedValue); | ||||||
| return storedValue; | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.