Skip to content

Commit

Permalink
feat: CDP Internal events processor (#27007)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
benjackwhite and github-actions[bot] authored Dec 27, 2024
1 parent e56f52b commit dd74b2c
Show file tree
Hide file tree
Showing 54 changed files with 1,027 additions and 378 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { ActivityScope, AvailableFeature } from '~/types'

import { SidePanelPaneHeader } from '../../components/SidePanelPaneHeader'
import { SidePanelActivityMetalytics } from './SidePanelActivityMetalytics'
import { SidePanelActivitySubscriptions } from './SidePanelActivitySubscriptions'

const SCROLL_TRIGGER_OFFSET = 100

Expand Down Expand Up @@ -152,6 +153,14 @@ export const SidePanelActivity = (): JSX.Element => {
},
]
: []),
...(featureFlags[FEATURE_FLAGS.CDP_ACTIVITY_LOG_NOTIFICATIONS]
? [
{
key: SidePanelActivityTab.Subscriptions,
label: 'Subscriptions',
},
]
: []),
]}
/>
</div>
Expand Down Expand Up @@ -280,6 +289,8 @@ export const SidePanelActivity = (): JSX.Element => {
</>
) : activeTab === SidePanelActivityTab.Metalytics ? (
<SidePanelActivityMetalytics />
) : activeTab === SidePanelActivityTab.Subscriptions ? (
<SidePanelActivitySubscriptions />
) : null}
</ScrollableShadows>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { LinkedHogFunctions } from 'scenes/pipeline/hogfunctions/list/LinkedHogFunctions'

export function SidePanelActivitySubscriptions(): JSX.Element {
return (
<div className="space-y-4 ">
<p>Get notified of your team's activity</p>

<LinkedHogFunctions
type="internal_destination"
subTemplateId="activity-log"
filters={{
events: [
{
id: `$activity_log_entry_created`,
type: 'events',
},
],
}}
/>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum SidePanelActivityTab {
Unread = 'unread',
All = 'all',
Metalytics = 'metalytics',
Subscriptions = 'subscriptions',
}

export const sidePanelActivityLogic = kea<sidePanelActivityLogicType>([
Expand All @@ -65,6 +66,7 @@ export const sidePanelActivityLogic = kea<sidePanelActivityLogicType>([
reducers({
activeTab: [
SidePanelActivityTab.Unread as SidePanelActivityTab,
{ persist: true },
{
setActiveTab: (_, { tab }) => tab,
},
Expand Down
17 changes: 10 additions & 7 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
GroupListParams,
HogFunctionIconResponse,
HogFunctionStatus,
HogFunctionSubTemplateIdType,
HogFunctionTemplateType,
HogFunctionType,
HogFunctionTypeType,
Expand Down Expand Up @@ -1824,13 +1825,15 @@ const api = {
): Promise<AppMetricsTotalsV2Response> {
return await new ApiRequest().hogFunction(id).withAction('metrics/totals').withQueryString(params).get()
},
async listTemplates(
type?: HogFunctionTypeType | HogFunctionTypeType[]
): Promise<PaginatedResponse<HogFunctionTemplateType>> {
return new ApiRequest()
.hogFunctionTemplates()
.withQueryString(Array.isArray(type) ? { types: type.join(',') } : { type: type ?? 'destination' })
.get()
async listTemplates(params: {
types: HogFunctionTypeType[]
sub_template_id?: HogFunctionSubTemplateIdType
}): Promise<PaginatedResponse<HogFunctionTemplateType>> {
const finalParams = {
...params,
types: params.types.join(','),
}
return new ApiRequest().hogFunctionTemplates().withQueryString(finalParams).get()
},
async getTemplate(id: HogFunctionTemplateType['id']): Promise<HogFunctionTemplateType> {
return await new ApiRequest().hogFunctionTemplate(id).get()
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 @@ -234,6 +234,7 @@ export const FEATURE_FLAGS = {
EXPERIMENT_STATS_V2: 'experiment-stats-v2', // owner: @danielbachhuber #team-experiments
WEB_ANALYTICS_PERIOD_COMPARISON: 'web-analytics-period-comparison', // owner: @rafaeelaudibert #team-web-analytics
WEB_ANALYTICS_CONVERSION_GOAL_FILTERS: 'web-analytics-conversion-goal-filters', // owner: @rafaeelaudibert #team-web-analytics
CDP_ACTIVITY_LOG_NOTIFICATIONS: 'cdp-activity-log-notifications', // owner: #team-cdp
COOKIELESS_SERVER_HASH_MODE_SETTING: 'cookieless-server-hash-mode-setting', // owner: @robbie-c #team-web-analytics
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/mocks/fixtures/_hogFunctionTemplates.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{
"sub_templates": [
{
"id": "early_access_feature_enrollment",
"id": "early-access-feature-enrollment",
"name": "Post to Slack on feature enrollment",
"description": "Posts a message to Slack when a user enrolls or un-enrolls in an early access feature",
"filters": { "events": [{ "id": "$feature_enrollment_update", "type": "events" }] },
Expand Down Expand Up @@ -35,7 +35,7 @@
}
},
{
"id": "survey_response",
"id": "survey-response",
"name": "Post to Slack on survey response",
"description": "Posts a message to Slack when a user responds to a survey",
"filters": {
Expand Down Expand Up @@ -171,15 +171,15 @@
{
"sub_templates": [
{
"id": "early_access_feature_enrollment",
"id": "early-access-feature-enrollment",
"name": "HTTP Webhook on feature enrollment",
"description": null,
"filters": { "events": [{ "id": "$feature_enrollment_update", "type": "events" }] },
"masking": null,
"inputs": null
},
{
"id": "survey_response",
"id": "survey-response",
"name": "HTTP Webhook on survey response",
"description": null,
"filters": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ export function EarlyAccessFeature({ id }: { id?: string } = {}): JSX.Element {
</div>
)}
</div>
<div className="flex flex-wrap gap-4 items-start">
<div className="flex flex-wrap items-start gap-4">
<div className="flex-1 min-w-[20rem]">
{isEditingFeature || isNewEarlyAccessFeature ? (
<LemonField name="description" label="Description" showOptional>
Expand Down Expand Up @@ -333,14 +333,14 @@ export function EarlyAccessFeature({ id }: { id?: string } = {}): JSX.Element {
<LinkedHogFunctions
type="destination"
filters={destinationFilters}
subTemplateId="early_access_feature_enrollment"
subTemplateId="early-access-feature-enrollment"
/>
</>
)}
{!isEditingFeature && !isNewEarlyAccessFeature && 'id' in earlyAccessFeature && (
<>
<LemonDivider className="my-8" />
<div className="flex items-start justify-between gap-4">
<div className="flex items-start justify-between gap-4">
<div>
<h3>Users</h3>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const newDestinationsLogic = kea<newDestinationsLogicType>([
const destinationTypes = siteDesinationsEnabled
? props.types
: props.types.filter((type) => type !== 'site_destination')
const templates = await api.hogFunctions.listTemplates(destinationTypes)
const templates = await api.hogFunctions.listTemplates({ types: destinationTypes })
return templates.results.reduce((acc, template) => {
acc[template.id] = template
return acc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
LemonDropdown,
LemonInput,
LemonLabel,
LemonSelect,
LemonSwitch,
LemonTag,
LemonTextArea,
Expand Down Expand Up @@ -42,9 +41,23 @@ const EVENT_THRESHOLD_ALERT_LEVEL = 8000
export interface HogFunctionConfigurationProps {
templateId?: string | null
id?: string | null

displayOptions?: {
showFilters?: boolean
showExpectedVolume?: boolean
showStatus?: boolean
showEnabled?: boolean
showTesting?: boolean
canEditSource?: boolean
showPersonsCount?: boolean
}
}

export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigurationProps): JSX.Element {
export function HogFunctionConfiguration({
templateId,
id,
displayOptions = {},
}: HogFunctionConfigurationProps): JSX.Element {
const logicProps = { templateId, id }
const logic = hogFunctionConfigurationLogic(logicProps)
const {
Expand All @@ -66,9 +79,7 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur
personsCountLoading,
personsListQuery,
template,
subTemplate,
templateHasChanged,
forcedSubTemplateId,
type,
} = useValues(logic)
const {
Expand All @@ -80,7 +91,6 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur
duplicateFromTemplate,
setConfigurationValue,
deleteHogFunction,
setSubTemplateId,
} = useActions(logic)

if (loading && !loaded) {
Expand Down Expand Up @@ -152,13 +162,24 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur
return <PayGateMini feature={AvailableFeature.DATA_PIPELINES} />
}

const showFilters = ['destination', 'site_destination', 'broadcast', 'transformation'].includes(type)
const showExpectedVolume = ['destination', 'site_destination'].includes(type)
const showStatus = ['destination', 'email', 'transformation'].includes(type)
const showEnabled = ['destination', 'email', 'site_destination', 'site_app', 'transformation'].includes(type)
const canEditSource = ['destination', 'email', 'site_destination', 'site_app', 'transformation'].includes(type)
const showPersonsCount = ['broadcast'].includes(type)
const showTesting = ['destination', 'transformation', 'broadcast', 'email'].includes(type)
const showFilters =
displayOptions.showFilters ??
['destination', 'internal_destination', 'site_destination', 'broadcast', 'transformation'].includes(type)
const showExpectedVolume = displayOptions.showExpectedVolume ?? ['destination', 'site_destination'].includes(type)
const showStatus =
displayOptions.showStatus ?? ['destination', 'internal_destination', 'email', 'transformation'].includes(type)
const showEnabled =
displayOptions.showEnabled ??
['destination', 'internal_destination', 'email', 'site_destination', 'site_app', 'transformation'].includes(
type
)
const canEditSource =
displayOptions.canEditSource ??
['destination', 'email', 'site_destination', 'site_app', 'transformation'].includes(type)
const showPersonsCount = displayOptions.showPersonsCount ?? ['broadcast'].includes(type)
const showTesting =
displayOptions.showTesting ??
['destination', 'internal_destination', 'transformation', 'broadcast', 'email'].includes(type)

return (
<div className="space-y-3">
Expand Down Expand Up @@ -359,41 +380,6 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur
</div>

<div className="space-y-4 flex-2 min-w-100">
{!forcedSubTemplateId && template?.sub_templates && (
<>
<div className="p-3 space-y-2 border rounded bg-bg-light">
<div className="flex items-center gap-2">
<LemonLabel className="flex-1">Choose template</LemonLabel>
<LemonSelect
size="small"
options={[
{
value: null,
label: 'Default',
},
...template.sub_templates.map((subTemplate) => ({
value: subTemplate.id,
label: subTemplate.name,
labelInMenu: (
<div className="my-1 space-y-1 max-w-120">
<div className="font-semibold">{subTemplate.name}</div>
<div className="font-sans text-xs text-muted">
{subTemplate.description}
</div>
</div>
),
})),
]}
value={subTemplate?.id}
onChange={(value) => {
setSubTemplateId(value)
}}
/>
</div>
</div>
</>
)}

<div className="p-3 space-y-2 border rounded bg-bg-light">
<div className="space-y-2">
<HogFunctionInputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { NodeKind } from '~/queries/schema'
import { AnyPropertyFilter, EntityTypes, FilterType, HogFunctionFiltersType } from '~/types'

import { hogFunctionConfigurationLogic } from '../hogFunctionConfigurationLogic'
import { HogFunctionFiltersInternal } from './HogFunctionFiltersInternal'

function sanitizeActionFilters(filters?: FilterType): Partial<HogFunctionFiltersType> {
if (!filters) {
Expand Down Expand Up @@ -74,6 +75,10 @@ export function HogFunctionFilters(): JSX.Element {
)
}

if (type === 'internal_destination') {
return <HogFunctionFiltersInternal />
}

const showMasking = type === 'destination'
const showDropEvents = type === 'transformation'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { LemonSelect } from '@posthog/lemon-ui'
import { LemonField } from 'lib/lemon-ui/LemonField'

import { HogFunctionFiltersType } from '~/types'

// NOTE: This is all a bit WIP and will be improved upon over time
// TODO: Make this more advanced with sub type filtering etc.
// TODO: Make it possible for the renderer to limit the options based on the type
const FILTER_OPTIONS = [
{
label: 'Team activity',
value: '$activity_log_entry_created',
},
]

const getSimpleFilterValue = (value?: HogFunctionFiltersType): string | undefined => {
return value?.events?.[0]?.id
}

const setSimpleFilterValue = (value: string): HogFunctionFiltersType => {
return {
events: [
{
name: FILTER_OPTIONS.find((option) => option.value === value)?.label,
id: value,
type: 'events',
},
],
}
}

export function HogFunctionFiltersInternal(): JSX.Element {
return (
<div className="p-3 space-y-2 border rounded bg-bg-light">
<LemonField name="filters" label="Trigger" help="Choose what event should trigger this destination">
{({ value, onChange }) => (
<>
<LemonSelect
options={FILTER_OPTIONS}
value={getSimpleFilterValue(value)}
onChange={(value) => onChange(setSimpleFilterValue(value))}
placeholder="Select a filter"
/>
</>
)}
</LemonField>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { hogFunctionConfigurationLogic } from './hogFunctionConfigurationLogic'
const HOG_TEMPLATE: HogFunctionTemplateType = {
sub_templates: [
{
id: 'early_access_feature_enrollment',
id: 'early-access-feature-enrollment',
name: 'HTTP Webhook on feature enrollment',
description: null,
filters: {
Expand All @@ -38,7 +38,7 @@ const HOG_TEMPLATE: HogFunctionTemplateType = {
inputs: null,
},
{
id: 'survey_response',
id: 'survey-response',
name: 'HTTP Webhook on survey response',
description: null,
filters: {
Expand Down
Loading

0 comments on commit dd74b2c

Please sign in to comment.