-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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(experiments): Properly translate event and action filters #28662
feat(experiments): Properly translate event and action filters #28662
Conversation
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.
PR Summary
This PR adds functionality to properly translate event and action filters into metric configuration objects for experiments in PostHog. Here are the key changes:
- Added new
ExperimentActionMetricConfig
type and interface inschema-general.ts
to support action-based metrics in experiments - Introduced
filterToMetricConfig
utility function to convert event/action filters into metric configurations, preserving properties like math operations and HogQL expressions - Added
metricConfigToFilter
utility function for the reverse conversion from metric configs back to filter format - Updated
ExperimentMetricForm
andSharedExperimentMetricForm
components to use the new conversion utilities instead of direct metric construction - Added comprehensive test coverage for the new filter translation functions, including property preservation and type safety checks
The changes improve type safety and maintainability by centralizing the conversion logic in dedicated utility functions.
5 file(s) reviewed, 7 comment(s)
Edit PR Review Bot Settings | Greptile
const entity = events?.[0] || actions?.[0] | ||
if (entity) { | ||
const metricConfig = filterToMetricConfig(entity as EventsNode | ActionsNode) |
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.
logic: Type assertion to EventsNode | ActionsNode could fail if entity is undefined - consider adding null check
const entity = events?.[0] || actions?.[0] | |
if (entity) { | |
const metricConfig = filterToMetricConfig(entity as EventsNode | ActionsNode) | |
const entity = events?.[0] || actions?.[0] | |
if (!entity) return | |
const metricConfig = filterToMetricConfig(entity as EventsNode | ActionsNode) |
math_property: undefined, | ||
math_hogql: undefined, | ||
properties: [{ key: '$lib', type: 'event', value: ['python'], operator: 'exact' }], | ||
kind: NodeKind.EventsNode, |
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.
logic: kind is set to EventsNode for an action type filter - this seems incorrect and inconsistent with the type declaration
const action = { | ||
id: '8', | ||
name: 'jan-16-running payment action', | ||
kind: 'EventsNode', |
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.
style: kind is set to 'EventsNode' as string instead of using NodeKind.EventsNode enum
kind: 'EventsNode', | |
kind: NodeKind.EventsNode, |
const entity = events?.[0] || actions?.[0] | ||
if (entity) { | ||
const metricConfig = filterToMetricConfig(entity as EventsNode | ActionsNode) |
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.
logic: Type assertion on line 49 could fail if entity is undefined since line 48 doesn't check for null/undefined
const entity = events?.[0] || actions?.[0] | |
if (entity) { | |
const metricConfig = filterToMetricConfig(entity as EventsNode | ActionsNode) | |
const entity = events?.[0] || actions?.[0] | |
if (!entity) return | |
const metricConfig = filterToMetricConfig(entity as EventsNode | ActionsNode) |
}, | ||
id: metric_config.action, | ||
name: metric_config.name, | ||
kind: NodeKind.EventsNode, |
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.
logic: incorrect node kind for actions - should be NodeKind.ActionsNode instead of NodeKind.EventsNode
kind: NodeKind.EventsNode, | |
kind: NodeKind.ActionsNode, |
export function metricConfigToFilter( | ||
metric_config: ExperimentEventMetricConfig | ExperimentActionMetricConfig | ExperimentDataWarehouseMetricConfig | ||
): FilterType { | ||
if (metric_config.kind === NodeKind.ExperimentEventMetricConfig) { | ||
return { | ||
events: [ | ||
{ | ||
id: '$pageview', | ||
name: '$pageview', | ||
id: metric_config.event, | ||
name: metric_config.event, | ||
kind: NodeKind.EventsNode, | ||
type: 'events', | ||
math: 'total', | ||
math_property: null, | ||
math_hogql: null, | ||
}, | ||
math: metric_config.math, | ||
math_property: metric_config.math_property, | ||
math_hogql: metric_config.math_hogql, | ||
properties: metric_config.properties, | ||
} as EventsNode, | ||
], | ||
actions: [], | ||
} | ||
} | ||
|
||
const config = metric.metric_config | ||
if (config.kind === NodeKind.ExperimentEventMetricConfig) { | ||
} else if (metric_config.kind === NodeKind.ExperimentActionMetricConfig) { | ||
return { | ||
events: [ | ||
events: [], | ||
actions: [ | ||
{ | ||
id: config.event, | ||
name: config.event, | ||
type: 'events', | ||
math: config.math, | ||
math_property: config.math_property, | ||
math_hogql: config.math_hogql, | ||
}, | ||
id: metric_config.action, | ||
name: metric_config.name, | ||
kind: NodeKind.EventsNode, | ||
type: 'actions', | ||
math: metric_config.math, | ||
math_property: metric_config.math_property, | ||
math_hogql: metric_config.math_hogql, | ||
properties: metric_config.properties, | ||
} as EventsNode, | ||
], | ||
actions: [], | ||
} | ||
} | ||
|
||
return {} | ||
} |
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.
logic: missing handling for ExperimentDataWarehouseMetricConfig case despite being in the function signature
return undefined | ||
} | ||
|
||
if (entity.kind === NodeKind.EventsNode) { |
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.
style: consider adding a type guard here instead of using Record<string, any>
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.
Awesome! Tested locally and stores metrics as expected 👍
properties?: AnyPropertyFilter[] | ||
} | ||
|
||
export interface ExperimentActionMetricConfig { |
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.
One thought: We could remove event
/ action
from these two schemas, and add id
and type: 'events' | 'action
. It will match what we get from the form then. We will get away with one schema and simplify metricConfigToFilter
logic. Not a strong opinion, but it will be less code.
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.
@andehen Potentially! I think I'd like to keep them separate for now, as there's some value in having them explicitly defined as such.
382fb93
into
init-new-experiment-query-runner
Merges into #28347
Changes
Properly translates event and action filters into their corresponding metric config objects.
Event
CleanShot.2025-02-13.at.05.12.49.mp4
Action
CleanShot.2025-02-13.at.05.14.44.mp4
Shared metric
CleanShot.2025-02-13.at.05.19.25.mp4
How did you test this code?
I added an event and an action to an experiment and verified they saved as expected.
I also created a shared metric for an action and a shared metric for a filter and verified they saved as expected.