-
Notifications
You must be signed in to change notification settings - Fork 40
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
fix(flags): send feature_flag_called
events with correct payload data in the event that we need to fetch the payloads from the server
#315
Changes from 5 commits
1093c2d
4a8a30c
84e89d6
16e3988
58bec8a
9d805fd
83419cb
9e67950
7b78ceb
745e622
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 |
---|---|---|
|
@@ -287,59 +287,82 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 { | |
disableGeoip?: boolean | ||
} | ||
): Promise<JsonType | undefined> { | ||
const { groups, disableGeoip } = options || {} | ||
let { onlyEvaluateLocally, sendFeatureFlagEvents, personProperties, groupProperties } = options || {} | ||
const { groups, disableGeoip, onlyEvaluateLocally = false, personProperties, groupProperties } = options || {} | ||
|
||
const adjustedProperties = this.addLocalPersonAndGroupProperties( | ||
const { allPersonProperties, allGroupProperties } = this.addLocalPersonAndGroupProperties( | ||
distinctId, | ||
groups, | ||
personProperties, | ||
groupProperties | ||
) | ||
|
||
personProperties = adjustedProperties.allPersonProperties | ||
groupProperties = adjustedProperties.allGroupProperties | ||
|
||
let response = undefined | ||
|
||
// Try to get match value locally if not provided | ||
if (!matchValue) { | ||
if (matchValue === undefined) { | ||
matchValue = await this.getFeatureFlag(key, distinctId, { | ||
...options, | ||
onlyEvaluateLocally: true, | ||
sendFeatureFlagEvents: false, | ||
}) | ||
} | ||
|
||
if (matchValue) { | ||
response = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue) | ||
} | ||
let response: string | boolean | undefined | ||
let payload: JsonType | undefined | ||
|
||
// set defaults | ||
if (onlyEvaluateLocally == undefined) { | ||
onlyEvaluateLocally = false | ||
} | ||
if (sendFeatureFlagEvents == undefined) { | ||
sendFeatureFlagEvents = true | ||
if (matchValue) { | ||
response = matchValue | ||
payload = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue) | ||
} else { | ||
response = undefined | ||
payload = undefined | ||
} | ||
|
||
// set defaults | ||
if (onlyEvaluateLocally == undefined) { | ||
onlyEvaluateLocally = false | ||
} | ||
// Determine if the payload was evaluated locally | ||
const payloadWasLocallyEvaluated = payload !== undefined | ||
|
||
const payloadWasLocallyEvaluated = response !== undefined | ||
// Fetch final flags and payloads either locally or from the remote server | ||
let fetchedOrLocalFlags: Record<string, string | boolean> | undefined | ||
let fetchedOrLocalPayloads: Record<string, JsonType | undefined> | undefined | ||
|
||
if (!payloadWasLocallyEvaluated && !onlyEvaluateLocally) { | ||
response = await super.getFeatureFlagPayloadStateless( | ||
key, | ||
if (payloadWasLocallyEvaluated || onlyEvaluateLocally) { | ||
if (response !== undefined) { | ||
fetchedOrLocalFlags = { [key]: response } | ||
fetchedOrLocalPayloads = { [key]: payload } | ||
} else { | ||
fetchedOrLocalFlags = {} | ||
fetchedOrLocalPayloads = {} | ||
} | ||
} else { | ||
const fetchedData = await super.getFeatureFlagsAndPayloadsStateless( | ||
distinctId, | ||
groups, | ||
personProperties, | ||
groupProperties, | ||
allPersonProperties, | ||
allGroupProperties, | ||
disableGeoip | ||
) | ||
fetchedOrLocalFlags = fetchedData.flags || {} | ||
fetchedOrLocalPayloads = fetchedData.payloads || {} | ||
} | ||
return response | ||
|
||
// Extract the final response and payload for the specific key | ||
const finalResponse = fetchedOrLocalFlags[key] | ||
const finalPayload = fetchedOrLocalPayloads[key] | ||
const finalLocallyEvaluated = payloadWasLocallyEvaluated | ||
|
||
this.capture({ | ||
distinctId, | ||
event: '$feature_flag_called', | ||
properties: { | ||
$feature_flag: key, | ||
$feature_flag_response: finalResponse, | ||
$feature_flag_payload: finalPayload, | ||
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. is this flag only for local evaluation? I don't think we send this one for any other Mobile SDK, but we do support feature flag payloads 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.
Not sure what you mean by that; what I did here is add a new property to the 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 I meant is that I did not see this new property 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. So my question about local evaluation is if this is only for backend SDKs (only backend SDKs have local evaluation afaik), because I have not found any reference on the JS SDK 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. AH I see! Okay this is a good point to raise: yes; right now this fallback on happens for local evaluation on backend SDKs, and we don't include any payload information in these events. In fact, I don't even think we send
where
So this is already feature gap between client-side and server side SDKs in more ways than just including the payload – our server-side SDKs send 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.
I think the reason is that before you read payloads, you read the flag using 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. yeah, I think that's a common pattern for the client-side SDKs that the server-side ones may not follow as much (e.g., the reason I started working on this change at all was because we have a user that's using our python SDK only for the Okay, so here's what I'm going to do going forward:
Thanks for chatting about it with me! |
||
locally_evaluated: finalLocallyEvaluated, | ||
[`$feature/${key}`]: finalResponse, | ||
}, | ||
groups, | ||
disableGeoip, | ||
}) | ||
|
||
// Return the final payload | ||
return finalPayload | ||
} | ||
|
||
async isFeatureEnabled( | ||
|
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.
explicitly don't send events when we evaluate the feature flag (since these events won't have response data for the payload), we'll send them manually from this method insstead.