-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: [add syncInit method] (FF-2431) #81
Conversation
2e5636d
to
8c18a65
Compare
8c18a65
to
2e12c7b
Compare
@@ -16,7 +16,7 @@ getIntegerAssignment(flagKey: string, subjectKey: string, subjectAttributes: Rec | |||
| --- | --- | --- | | |||
| flagKey | string | | | |||
| subjectKey | string | | | |||
| subjectAttributes | Record<string, any> | | | |||
| subjectAttributes | Record<string, AttributeType> | | |
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.
I don't get why this is changing - I'm assuming someone else is checking in doc changes without this?
src/index.spec.ts
Outdated
describe('sync init', () => { | ||
it('initializes with flags in obfuscated mode', () => { | ||
const client = initSync({ | ||
isObfuscated: true, | ||
flagsConfiguration: { | ||
[obfuscatedFlagKey]: mockObfuscatedUfcFlagConfig, | ||
}, | ||
}); | ||
|
||
expect(client.getStringAssignment(flagKey, 'subject-10', {}, 'default-value')).toEqual( | ||
'variant-1', | ||
); | ||
}); | ||
|
||
it('initializes with flags in not-obfuscated mode', () => { | ||
const client = initSync({ | ||
isObfuscated: false, | ||
flagsConfiguration: { | ||
[flagKey]: mockNotObfuscatedFlagConfig, | ||
}, | ||
}); | ||
|
||
expect(client.getStringAssignment(flagKey, 'subject-10', {}, 'default-value')).toEqual( | ||
'variant-1', | ||
); | ||
}); | ||
}); | ||
|
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.
new tests for the functionality being added in this PR
@@ -47,7 +55,64 @@ const obfuscatedFlagKey = md5Hash(flagKey); | |||
const allocationKey = 'traffic-split'; | |||
const obfuscatedAllocationKey = base64Encode(allocationKey); | |||
|
|||
const mockUfcFlagConfig: Flag = { |
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.
Renamed this to be called obfuscated
for clarity
src/index.ts
Outdated
export interface IClientConfigSync { | ||
flagsConfiguration: Record<string, Flag>; | ||
|
||
assignmentLogger?: IAssignmentLogger; | ||
|
||
isObfuscated?: boolean; | ||
|
||
throwOnFailedInitialization?: boolean; | ||
} | ||
|
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.
These are the possible configuration options for calling the initSync
method - note that no SDK key is provided as it will be operating in off-line mode.
Assuming that on the server the user stringified the configurations to a JSON string, they will need to parse it back into the object using the types we export in the SDK.
src/index.ts
Outdated
} | ||
|
||
// There is no SDK key in the offline context. | ||
const storageKeySuffix = buildStorageKeySuffix('no_api_key'); |
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.
Got another idea for handling this?
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.
just use a random string instead, I think that would be cleaner
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.
The only thing I don't love about this:
- generate a random string in JS seems like so much code
[...Array(8)].map(() => Math.random().toString(36)[2]).join('');
but that's fine - if we move to having a persistent store i don't want to generate a different random string each initialization which would make the store useless. perhaps a different static string like
offline
is more clear?
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.
yeah I think either should be fine, as long as the intention is clear. could be as simple as extracting to a function like generateStorageCacheKeySuffixForOffline
or something like that
// As this is a synchronous initialization, | ||
// we are unable to call the async `init` method on the assignment cache | ||
// which loads the assignment cache from the browser's storage. | ||
// Therefore there is no purpose trying to use a persistent assignment cache. | ||
const assignmentCache = assignmentCacheFactory({ | ||
storageKeySuffix, | ||
forceMemoryOnly: true, | ||
}); | ||
EppoJSClient.instance.useCustomAssignmentCache(assignmentCache); |
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.
There is still value to have an in-memory cache as it will remove duplicates for the duration of the session. Perhaps we can think creatively how to support this.
// Default to obfuscated mode when requesting configuration from the server. | ||
instance.setIsObfuscated(true); |
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.
Making it clear here.
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.
Main feedback is around the assignment cache/config cache usage, otherwise looks good to me
// configuration from your server SDK | ||
const configurationJsonString: string = getConfigurationFromServer(); | ||
// The configuration will be not-obfuscated from your server SDK. If you have obfuscated flag values, you can use the `ObfuscatedFlag` type. | ||
const flagsConfiguration: Record<string, Flag | ObfuscatedFlag> = JSON.parse(configurationJsonString); |
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.
nit: I think i would omit the types for brevity since they are inferred anyways
const simpleCache = new SimpleAssignmentCache(); | ||
if (forceMemoryOnly) { |
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.
instead of doing this, which makes this method more complex (unecessarily, I think), i'd just instantiate SimpleAssignmentCache
directly from syncInit
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.
Yea - I don't have strong feelings about it either way. On the one hand this method becomes the central way to instantiate caches without the caller needing to know anything about the SimpleAssignmentCache
besides saying they want an inMemory
.
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.
i feel like it's an unnecessary abstraction that adds complexity. not gonna block on it, but it's my personal preference ;)
src/index.ts
Outdated
} | ||
|
||
// There is no SDK key in the offline context. | ||
const storageKeySuffix = buildStorageKeySuffix('no_api_key'); |
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.
just use a random string instead, I think that would be cleaner
Fixes: #issue
Motivation and Context
Users need to be able to bootstrap the JS SDK from configuration obtained from their server, or another external process. The format of this configuration might or might not be obfuscated.
Furthermore users need to be able to instantiate the SDK synchronously.
Description
syncInit
- it accepts a map of flags in both obfuscated and not-obfuscated formats. pairs with thesetIsObfuscated
parameter to make the SDK understand the incoming data.How has this been tested?
New unit tests to validate behavior of providing obfuscated and not-obfuscated configuration.