Skip to content

Commit

Permalink
feat(web-analytics): Session filters 1 (#21512)
Browse files Browse the repository at this point in the history
* Handle hogql session properties

* Add test for two lazy joins in one query (session & person)

* Support passing session properties in web analytics queries

* Fix typings

* Working session definition fetching

* Working session property definitions

* Remove property allow list in web analytics

* Put session properties first

* Add session values

* Rename duration back to $session_duration

* Fix "keeps infiniteListCounts in sync"

* Fix "setting search query filters events"

* Fix taxonomy tests

* Change session api to GenericViewSet

* Add local properties back

* Support datetime and boolean session properties

* Update mypy baseline

* Hide duration as a property and asterisk field

* Add AsyncReturnType

* Make taxonoicFilterLogic tests more reliable (maybe)

* Fix duplicates from bad rebase

* Only show session table session properties if feature flag enabled

* Improve error message for non-hogql non-duration session property

* Put session table properties behind FF

* Revert taxonomicFilterLogic tests

* Update query snapshots

* Rename TaxonomicFilterGroupType.Sessions to TaxonomicFilterGroupType.SessionProperties

* Use ViewSet instead of GenericViewSet

* Add tests for session properties and session property values api

* Formatting

* New typing

* Update query snapshots

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
robbie-c and github-actions[bot] authored Apr 25, 2024
1 parent 3a7646b commit e3acb16
Show file tree
Hide file tree
Showing 38 changed files with 641 additions and 136 deletions.
21 changes: 21 additions & 0 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ class ApiRequest {
.withQueryString(queryParams)
}

public sessionPropertyDefinitions(teamId?: TeamType['id']): ApiRequest {
return this.projectsDetail(teamId).addPathComponent('sessions').addPathComponent('property_definitions')
}

public dataManagementActivity(teamId?: TeamType['id']): ApiRequest {
return this.projectsDetail(teamId).addPathComponent('data_management').addPathComponent('activity')
}
Expand Down Expand Up @@ -1212,6 +1216,23 @@ const api = {
},
},

sessions: {
async propertyDefinitions({
teamId = ApiConfig.getCurrentTeamId(),
search,
properties,
}: {
teamId?: TeamType['id']
search?: string
properties?: string[]
}): Promise<CountedPaginatedResponse<PropertyDefinition>> {
return new ApiRequest()
.sessionPropertyDefinitions(teamId)
.withQueryString(toParams({ search, ...(properties ? { properties: properties.join(',') } : {}) }))
.get()
},
},

cohorts: {
async get(cohortId: CohortType['id']): Promise<CohortType> {
return await new ApiRequest().cohortsDetail(cohortId).get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ function Example({ value }: { value?: string }): JSX.Element {
type === TaxonomicFilterGroupType.EventFeatureFlags ||
type === TaxonomicFilterGroupType.PersonProperties ||
type === TaxonomicFilterGroupType.GroupsPrefix ||
type === TaxonomicFilterGroupType.Metadata
type === TaxonomicFilterGroupType.Metadata ||
type === TaxonomicFilterGroupType.SessionProperties
) {
data = getCoreFilterDefinition(value, type)
} else if (type === TaxonomicFilterGroupType.Elements) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function DefinitionView({ group }: { group: TaxonomicFilterGroup }): JSX.Element
isCohort,
isDataWarehouse,
isProperty,
hasSentAs,
} = useValues(definitionPopoverLogic)

const { setLocalDefinition } = useActions(definitionPopoverLogic)
Expand Down Expand Up @@ -142,13 +143,17 @@ function DefinitionView({ group }: { group: TaxonomicFilterGroup }): JSX.Element
/>
</DefinitionPopover.Grid>

<DefinitionPopover.HorizontalLine />
<DefinitionPopover.Section>
<DefinitionPopover.Card
title="Sent as"
value={<span className="font-mono text-xs">{_definition.name}</span>}
/>
</DefinitionPopover.Section>
{hasSentAs ? (
<>
<DefinitionPopover.HorizontalLine />
<DefinitionPopover.Section>
<DefinitionPopover.Card
title="Sent as"
value={<span className="font-mono text-xs">{_definition.name}</span>}
/>
</DefinitionPopover.Section>
</>
) : null}
</>
)
}
Expand Down Expand Up @@ -176,17 +181,21 @@ function DefinitionView({ group }: { group: TaxonomicFilterGroup }): JSX.Element
<DefinitionPopover.Grid cols={2}>
<DefinitionPopover.Card title="Property Type" value={_definition.property_type ?? '-'} />
</DefinitionPopover.Grid>
<DefinitionPopover.HorizontalLine />
<DefinitionPopover.Grid cols={2}>
<DefinitionPopover.Card
title="Sent as"
value={
<span className="truncate text-mono text-xs" title={_definition.name ?? undefined}>
{_definition.name !== '' ? _definition.name : <i>(empty string)</i>}
</span>
}
/>
</DefinitionPopover.Grid>
{hasSentAs ? (
<>
<DefinitionPopover.HorizontalLine />
<DefinitionPopover.Grid cols={2}>
<DefinitionPopover.Card
title="Sent as"
value={
<span className="truncate text-mono text-xs" title={_definition.name ?? undefined}>
{_definition.name !== '' ? _definition.name : <i>(empty string)</i>}
</span>
}
/>
</DefinitionPopover.Grid>
</>
) : null}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,17 @@ export const definitionPopoverLogic = kea<definitionPopoverLogicType>([
[
TaxonomicFilterGroupType.PersonProperties,
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.SessionProperties,
TaxonomicFilterGroupType.EventFeatureFlags,
TaxonomicFilterGroupType.NumericalEventProperties,
TaxonomicFilterGroupType.Metadata,
].includes(type) || type.startsWith(TaxonomicFilterGroupType.GroupsPrefix),
],
hasSentAs: [
(s) => [s.type, s.isProperty, s.isEvent],
(type, isProperty, isEvent) =>
isEvent || (isProperty && type !== TaxonomicFilterGroupType.SessionProperties),
],
isCohort: [(s) => [s.type], (type) => type === TaxonomicFilterGroupType.Cohorts],
isDataWarehouse: [(s) => [s.type], (type) => type === TaxonomicFilterGroupType.DataWarehouse],
viewFullDetailUrl: [
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/components/DefinitionPopover/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function getSingularType(type: TaxonomicFilterGroupType): string {
case TaxonomicFilterGroupType.EventProperties:
case TaxonomicFilterGroupType.PersonProperties:
case TaxonomicFilterGroupType.GroupsPrefix: // Group properties
case TaxonomicFilterGroupType.SessionProperties:
return 'property'
case TaxonomicFilterGroupType.EventFeatureFlags:
return 'feature'
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/components/PropertyFilters/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('propertyFilterTypeToTaxonomicFilterType()', () => {
...baseFilter,
type: PropertyFilterType.Session,
} as SessionPropertyFilter)
).toEqual(TaxonomicFilterGroupType.Sessions)
).toEqual(TaxonomicFilterGroupType.SessionProperties)
expect(propertyFilterTypeToTaxonomicFilterType({ ...baseFilter, type: PropertyFilterType.HogQL })).toEqual(
TaxonomicFilterGroupType.HogQLExpression
)
Expand Down Expand Up @@ -122,7 +122,7 @@ describe('breakdownFilterToTaxonomicFilterType()', () => {
TaxonomicFilterGroupType.EventProperties
)
expect(breakdownFilterToTaxonomicFilterType({ ...baseFilter, breakdown_type: 'session' })).toEqual(
TaxonomicFilterGroupType.Sessions
TaxonomicFilterGroupType.SessionProperties
)
expect(breakdownFilterToTaxonomicFilterType({ ...baseFilter, breakdown_type: 'hogql' })).toEqual(
TaxonomicFilterGroupType.HogQLExpression
Expand Down
16 changes: 11 additions & 5 deletions frontend/src/lib/components/PropertyFilters/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const PROPERTY_FILTER_TYPE_TO_TAXONOMIC_FILTER_GROUP_TYPE: Omit<
[PropertyFilterType.Feature]: TaxonomicFilterGroupType.EventFeatureFlags,
[PropertyFilterType.Cohort]: TaxonomicFilterGroupType.Cohorts,
[PropertyFilterType.Element]: TaxonomicFilterGroupType.Elements,
[PropertyFilterType.Session]: TaxonomicFilterGroupType.Sessions,
[PropertyFilterType.Session]: TaxonomicFilterGroupType.SessionProperties,
[PropertyFilterType.HogQL]: TaxonomicFilterGroupType.HogQLExpression,
[PropertyFilterType.Group]: TaxonomicFilterGroupType.GroupsPrefix,
[PropertyFilterType.DataWarehouse]: TaxonomicFilterGroupType.DataWarehouse,
Expand Down Expand Up @@ -183,10 +183,14 @@ export function isEventPropertyFilter(filter?: AnyFilterLike | null): filter is
export function isPersonPropertyFilter(filter?: AnyFilterLike | null): filter is PersonPropertyFilter {
return filter?.type === PropertyFilterType.Person
}
export function isEventPropertyOrPersonPropertyFilter(
export function isEventPersonOrSessionPropertyFilter(
filter?: AnyFilterLike | null
): filter is EventPropertyFilter | PersonPropertyFilter {
return filter?.type === PropertyFilterType.Event || filter?.type === PropertyFilterType.Person
): filter is EventPropertyFilter | PersonPropertyFilter | SessionPropertyFilter {
return (
filter?.type === PropertyFilterType.Event ||
filter?.type === PropertyFilterType.Person ||
filter?.type === PropertyFilterType.Session
)
}
export function isElementPropertyFilter(filter?: AnyFilterLike | null): filter is ElementPropertyFilter {
return filter?.type === PropertyFilterType.Element
Expand Down Expand Up @@ -264,7 +268,7 @@ const propertyFilterMapping: Partial<Record<PropertyFilterType, TaxonomicFilterG
[PropertyFilterType.Feature]: TaxonomicFilterGroupType.EventFeatureFlags,
[PropertyFilterType.Cohort]: TaxonomicFilterGroupType.Cohorts,
[PropertyFilterType.Element]: TaxonomicFilterGroupType.Elements,
[PropertyFilterType.Session]: TaxonomicFilterGroupType.Sessions,
[PropertyFilterType.Session]: TaxonomicFilterGroupType.SessionProperties,
[PropertyFilterType.HogQL]: TaxonomicFilterGroupType.HogQLExpression,
}

Expand Down Expand Up @@ -308,6 +312,8 @@ export function propertyFilterTypeToPropertyDefinitionType(
? PropertyDefinitionType.Person
: filterType === PropertyFilterType.Group
? PropertyDefinitionType.Group
: filterType === PropertyFilterType.Session
? PropertyDefinitionType.Session
: PropertyDefinitionType.Event
}

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const renderItemContents = ({
listGroupType === TaxonomicFilterGroupType.Events ||
listGroupType === TaxonomicFilterGroupType.CustomEvents ||
listGroupType === TaxonomicFilterGroupType.Metadata ||
listGroupType === TaxonomicFilterGroupType.SessionProperties ||
listGroupType.startsWith(TaxonomicFilterGroupType.GroupsPrefix) ? (
<>
<div className={clsx('taxonomic-list-row-contents', isStale && 'text-muted')}>
Expand Down Expand Up @@ -160,6 +161,7 @@ const selectedItemHasPopover = (
TaxonomicFilterGroupType.Cohorts,
TaxonomicFilterGroupType.CohortsWithAllUsers,
TaxonomicFilterGroupType.Metadata,
TaxonomicFilterGroupType.SessionProperties,
].includes(listGroupType) ||
listGroupType.startsWith(TaxonomicFilterGroupType.GroupsPrefix))
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('taxonomicFilterLogic', () => {
TaxonomicFilterGroupType.Events,
TaxonomicFilterGroupType.Actions,
TaxonomicFilterGroupType.Elements,
TaxonomicFilterGroupType.Sessions,
TaxonomicFilterGroupType.SessionProperties,
],
}
logic = taxonomicFilterLogic(logicProps)
Expand All @@ -62,7 +62,7 @@ describe('taxonomicFilterLogic', () => {
infiniteListLogic({ ...logic.props, listGroupType: TaxonomicFilterGroupType.Events }),
infiniteListLogic({ ...logic.props, listGroupType: TaxonomicFilterGroupType.Actions }),
infiniteListLogic({ ...logic.props, listGroupType: TaxonomicFilterGroupType.Elements }),
infiniteListLogic({ ...logic.props, listGroupType: TaxonomicFilterGroupType.Sessions }),
infiniteListLogic({ ...logic.props, listGroupType: TaxonomicFilterGroupType.SessionProperties }),
])
expect(
infiniteListLogic({ ...logic.props, listGroupType: TaxonomicFilterGroupType.Cohorts }).isMounted()
Expand All @@ -76,7 +76,7 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 1,
[TaxonomicFilterGroupType.Actions]: 0,
[TaxonomicFilterGroupType.Elements]: 4,
[TaxonomicFilterGroupType.Sessions]: 1,
[TaxonomicFilterGroupType.SessionProperties]: 1,
},
})
.toDispatchActions(['infiniteListResultsReceived'])
Expand All @@ -87,7 +87,7 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 157,
[TaxonomicFilterGroupType.Actions]: 0, // not mocked
[TaxonomicFilterGroupType.Elements]: 4,
[TaxonomicFilterGroupType.Sessions]: 1,
[TaxonomicFilterGroupType.SessionProperties]: 1,
},
})
})
Expand All @@ -110,7 +110,7 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 4,
[TaxonomicFilterGroupType.Actions]: 0,
[TaxonomicFilterGroupType.Elements]: 0,
[TaxonomicFilterGroupType.Sessions]: 0,
[TaxonomicFilterGroupType.SessionProperties]: 0,
},
})

Expand All @@ -127,7 +127,7 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 0,
[TaxonomicFilterGroupType.Actions]: 0,
[TaxonomicFilterGroupType.Elements]: 1,
[TaxonomicFilterGroupType.Sessions]: 0,
[TaxonomicFilterGroupType.SessionProperties]: 0,
},
})

Expand All @@ -144,7 +144,7 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 0,
[TaxonomicFilterGroupType.Actions]: 0,
[TaxonomicFilterGroupType.Elements]: 0,
[TaxonomicFilterGroupType.Sessions]: 0,
[TaxonomicFilterGroupType.SessionProperties]: 0,
},
})

Expand All @@ -161,13 +161,13 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 157,
[TaxonomicFilterGroupType.Actions]: 0,
[TaxonomicFilterGroupType.Elements]: 4,
[TaxonomicFilterGroupType.Sessions]: 1,
[TaxonomicFilterGroupType.SessionProperties]: 1,
},
})

// move right, skipping Actions
await expectLogic(logic, () => logic.actions.tabRight()).toMatchValues({
activeTab: TaxonomicFilterGroupType.Sessions,
activeTab: TaxonomicFilterGroupType.SessionProperties,
})
await expectLogic(logic, () => logic.actions.tabRight()).toMatchValues({
activeTab: TaxonomicFilterGroupType.Events,
Expand All @@ -181,7 +181,7 @@ describe('taxonomicFilterLogic', () => {
activeTab: TaxonomicFilterGroupType.Events,
})
await expectLogic(logic, () => logic.actions.tabLeft()).toMatchValues({
activeTab: TaxonomicFilterGroupType.Sessions,
activeTab: TaxonomicFilterGroupType.SessionProperties,
})
await expectLogic(logic, () => logic.actions.tabLeft()).toMatchValues({
activeTab: TaxonomicFilterGroupType.Elements,
Expand All @@ -201,7 +201,7 @@ describe('taxonomicFilterLogic', () => {
[TaxonomicFilterGroupType.Events]: 4,
[TaxonomicFilterGroupType.Actions]: 0,
[TaxonomicFilterGroupType.Elements]: 0,
[TaxonomicFilterGroupType.Sessions]: 0,
[TaxonomicFilterGroupType.SessionProperties]: 0,
},
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
TaxonomicFilterLogicProps,
TaxonomicFilterValue,
} from 'lib/components/TaxonomicFilter/types'
import { FEATURE_FLAGS } from 'lib/constants'
import { IconCohort } from 'lib/lemon-ui/icons'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { CORE_FILTER_DEFINITIONS_BY_GROUP } from 'lib/taxonomy'
import { capitalizeFirstLetter, pluralize, toParams } from 'lib/utils'
import { getEventDefinitionIcon, getPropertyDefinitionIcon } from 'scenes/data-management/events/DefinitionHeader'
Expand Down Expand Up @@ -168,6 +170,7 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>([
s.metadataSource,
s.excludedProperties,
s.propertyAllowList,
featureFlagLogic.selectors.featureFlags,
],
(
teamId,
Expand All @@ -177,7 +180,8 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>([
schemaColumns,
metadataSource,
excludedProperties,
propertyAllowList
propertyAllowList,
featureFlags
): TaxonomicFilterGroup[] => {
const groups: TaxonomicFilterGroup[] = [
{
Expand Down Expand Up @@ -486,18 +490,26 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>([
getPopoverHeader: () => 'Notebooks',
},
{
name: 'Sessions',
name: 'Session Properties',
searchPlaceholder: 'sessions',
type: TaxonomicFilterGroupType.Sessions,
options: [
{
name: 'Session duration',
value: '$session_duration',
},
],
type: TaxonomicFilterGroupType.SessionProperties,
options: featureFlags[FEATURE_FLAGS.SESSION_TABLE_PROPERTY_FILTERS]
? undefined
: [
{
id: '$session_duration',
name: '$session_duration',
property_type: 'Duration',
is_numerical: true,
},
],
getName: (option: any) => option.name,
getValue: (option: any) => option.value,
getValue: (option) => option.name,
getPopoverHeader: () => 'Session',
endpoint: featureFlags[FEATURE_FLAGS.SESSION_TABLE_PROPERTY_FILTERS]
? `api/projects/${teamId}/sessions/property_definitions`
: undefined,
getIcon: getPropertyDefinitionIcon,
},
{
name: 'HogQL',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/TaxonomicFilter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export enum TaxonomicFilterGroupType {
Plugins = 'plugins',
Dashboards = 'dashboards',
GroupNamesPrefix = 'name_groups',
Sessions = 'sessions',
SessionProperties = 'session_properties',
HogQLExpression = 'hogql_expression',
Notebooks = 'notebooks',
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export const FEATURE_FLAGS = {
EMAIL_VERIFICATION_TICKET_SUBMISSION: 'email-verification-ticket-submission', // owner: #team-growth
TOOLBAR_HEATMAPS: 'toolbar-heatmaps', // owner: #team-replay
THEME: 'theme', // owner: @aprilfools
SESSION_TABLE_PROPERTY_FILTERS: 'session-table-property-filters', // owner: @robbie-c
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
Loading

0 comments on commit e3acb16

Please sign in to comment.