Skip to content

Commit

Permalink
feat(cdp): mapping (#26655)
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
mariusandra and github-actions[bot] authored Dec 12, 2024
1 parent a4e7396 commit bae2b6f
Show file tree
Hide file tree
Showing 25 changed files with 683 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { HogFunctionIconEditable } from './HogFunctionIcon'
import { HogFunctionInputs } from './HogFunctionInputs'
import { HogFunctionStatusIndicator } from './HogFunctionStatusIndicator'
import { HogFunctionTest, HogFunctionTestPlaceholder } from './HogFunctionTest'
import { HogFunctionMapping } from './mapping/HogFunctionMapping'

const EVENT_THRESHOLD_ALERT_LEVEL = 8000

Expand Down Expand Up @@ -396,7 +397,10 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur

<div className="border bg-bg-light rounded p-3 space-y-2">
<div className="space-y-2">
<HogFunctionInputs />
<HogFunctionInputs
configuration={configuration}
setConfigurationValue={setConfigurationValue}
/>
{showSource && canEditSource ? (
<LemonButton
icon={<IconPlus />}
Expand All @@ -421,6 +425,8 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur
</div>
</div>

<HogFunctionMapping />

{canEditSource && (
<div
className={clsx(
Expand Down
72 changes: 55 additions & 17 deletions frontend/src/scenes/pipeline/hogfunctions/HogFunctionInputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@ import {
LemonTextArea,
Tooltip,
} from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { useValues } from 'kea'
import { LemonField } from 'lib/lemon-ui/LemonField'
import { CodeEditorInline, CodeEditorInlineProps } from 'lib/monaco/CodeEditorInline'
import { CodeEditorResizeable } from 'lib/monaco/CodeEditorResizable'
import { capitalizeFirstLetter } from 'lib/utils'
import { useEffect, useState } from 'react'

import { HogFunctionInputSchemaType, HogFunctionInputType } from '~/types'
import {
HogFunctionConfigurationType,
HogFunctionInputSchemaType,
HogFunctionInputType,
HogFunctionMappingType,
} from '~/types'

import { EmailTemplater } from './email-templater/EmailTemplater'
import { hogFunctionConfigurationLogic } from './hogFunctionConfigurationLogic'
Expand All @@ -35,7 +40,14 @@ export type HogFunctionInputProps = {
disabled?: boolean
}

export interface HogFunctionInputsProps {
configuration: HogFunctionConfigurationType | HogFunctionMappingType
setConfigurationValue: (key: string, value: any) => void
}

export type HogFunctionInputWithSchemaProps = {
configuration: HogFunctionConfigurationType | HogFunctionMappingType
setConfigurationValue: (key: string, value: any) => void
schema: HogFunctionInputSchemaType
}

Expand Down Expand Up @@ -196,9 +208,15 @@ type HogFunctionInputSchemaControlsProps = {
value: HogFunctionInputSchemaType
onChange: (value: HogFunctionInputSchemaType | null) => void
onDone: () => void
supportsSecrets: boolean
}

function HogFunctionInputSchemaControls({ value, onChange, onDone }: HogFunctionInputSchemaControlsProps): JSX.Element {
function HogFunctionInputSchemaControls({
value,
onChange,
onDone,
supportsSecrets,
}: HogFunctionInputSchemaControlsProps): JSX.Element {
const _onChange = (data: Partial<HogFunctionInputSchemaType> | null): void => {
if (data?.key?.length === 0) {
setLocalVariableError('Input variable name cannot be empty')
Expand Down Expand Up @@ -230,13 +248,15 @@ function HogFunctionInputSchemaControls({ value, onChange, onDone }: HogFunction
label="Required"
bordered
/>
<LemonCheckbox
size="small"
checked={value.secret}
onChange={(secret) => _onChange({ secret })}
label="Secret"
bordered
/>
{supportsSecrets ? (
<LemonCheckbox
size="small"
checked={value.secret}
onChange={(secret) => _onChange({ secret })}
label="Secret"
bordered
/>
) : null}
<div className="flex-1" />
<LemonButton status="danger" icon={<IconTrash />} size="small" onClick={() => onChange(null)} />
<LemonButton type="secondary" size="small" onClick={() => onDone()}>
Expand Down Expand Up @@ -314,10 +334,13 @@ function HogFunctionInputSchemaControls({ value, onChange, onDone }: HogFunction
)
}

export function HogFunctionInputWithSchema({ schema }: HogFunctionInputWithSchemaProps): JSX.Element {
export function HogFunctionInputWithSchema({
schema,
configuration,
setConfigurationValue,
}: HogFunctionInputWithSchemaProps): JSX.Element {
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: schema.key })
const { showSource, configuration } = useValues(hogFunctionConfigurationLogic)
const { setConfigurationValue } = useActions(hogFunctionConfigurationLogic)
const { showSource } = useValues(hogFunctionConfigurationLogic)
const [editing, setEditing] = useState(false)

const value = configuration.inputs?.[schema.key]
Expand Down Expand Up @@ -349,6 +372,7 @@ export function HogFunctionInputWithSchema({ schema }: HogFunctionInputWithSchem
}, [showSource])

const supportsTemplating = ['string', 'json', 'dictionary', 'email'].includes(schema.type)
const supportsSecrets = 'type' in configuration // no secrets for mapping inputs

return (
<div
Expand Down Expand Up @@ -444,18 +468,25 @@ export function HogFunctionInputWithSchema({ schema }: HogFunctionInputWithSchem
value={schema}
onChange={onSchemaChange}
onDone={() => setEditing(false)}
supportsSecrets={supportsSecrets}
/>
</div>
)}
</div>
)
}

export function HogFunctionInputs(): JSX.Element {
const { showSource, configuration } = useValues(hogFunctionConfigurationLogic)
const { setConfigurationValue } = useActions(hogFunctionConfigurationLogic)
export function HogFunctionInputs({
configuration,
setConfigurationValue,
}: HogFunctionInputsProps): JSX.Element | null {
const { showSource } = useValues(hogFunctionConfigurationLogic)

if (!configuration?.inputs_schema?.length) {
if (!('type' in configuration)) {
// If this is a mapping, don't show any error message.
return null
}
return <span className="italic text-muted-alt">This function does not require any input variables.</span>
}

Expand All @@ -477,7 +508,14 @@ export function HogFunctionInputs(): JSX.Element {
>
<SortableContext disabled={!showSource} items={inputSchemaIds} strategy={verticalListSortingStrategy}>
{configuration.inputs_schema?.map((schema) => {
return <HogFunctionInputWithSchema key={schema.key} schema={schema} />
return (
<HogFunctionInputWithSchema
key={schema.key}
schema={schema}
configuration={configuration}
setConfigurationValue={setConfigurationValue}
/>
)
})}
</SortableContext>
</DndContext>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function sanitizeActionFilters(filters?: FilterType): Partial<HogFunctionFilters

export function HogFunctionFilters(): JSX.Element {
const { groupsTaxonomicTypes } = useValues(groupsModel)
const { configuration, type } = useValues(hogFunctionConfigurationLogic)
const { configuration, type, useMapping } = useValues(hogFunctionConfigurationLogic)

if (type === 'broadcast') {
return (
Expand Down Expand Up @@ -78,74 +78,87 @@ export function HogFunctionFilters(): JSX.Element {

return (
<div className="border bg-bg-light rounded p-3 space-y-2">
<LemonField name="filters" label="Filters">
{({ value, onChange }) => (
<>
<TestAccountFilterSwitch
checked={value?.filter_test_accounts ?? false}
onChange={(filter_test_accounts) => onChange({ ...value, filter_test_accounts })}
fullWidth
/>
<PropertyFilters
propertyFilters={value?.properties ?? []}
taxonomicGroupTypes={[
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.PersonProperties,
TaxonomicFilterGroupType.EventFeatureFlags,
TaxonomicFilterGroupType.Elements,
TaxonomicFilterGroupType.HogQLExpression,
]}
onChange={(properties: AnyPropertyFilter[]) => {
onChange({
...value,
properties,
})
}}
pageKey={`HogFunctionPropertyFilters.${id}`}
/>
<LemonField
name="filters"
label={useMapping ? 'Global filters' : 'Filters'}
info={useMapping ? 'Filters applied to all events before they reach a mapping' : null}
>
{({ value, onChange }) => {
const filters = (value ?? {}) as HogFunctionFiltersType
return (
<>
<TestAccountFilterSwitch
checked={filters?.filter_test_accounts ?? false}
onChange={(filter_test_accounts) => onChange({ ...filters, filter_test_accounts })}
fullWidth
/>
<PropertyFilters
propertyFilters={(filters?.properties ?? []) as AnyPropertyFilter[]}
taxonomicGroupTypes={[
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.PersonProperties,
TaxonomicFilterGroupType.EventFeatureFlags,
TaxonomicFilterGroupType.Elements,
TaxonomicFilterGroupType.HogQLExpression,
]}
onChange={(properties: AnyPropertyFilter[]) => {
onChange({
...filters,
properties,
})
}}
pageKey={`HogFunctionPropertyFilters.${id}`}
/>

<LemonLabel>Match event and actions</LemonLabel>
<p className="mb-0 text-muted-alt text-xs">
If set, the destination will only run if the <b>event matches any</b> of the below.
</p>
<ActionFilter
bordered
filters={value ?? {}}
setFilters={(payload) => {
onChange({
...value,
...sanitizeActionFilters(payload),
})
}}
typeKey="plugin-filters"
mathAvailability={MathAvailability.None}
hideRename
hideDuplicate
showNestedArrow={false}
actionsTaxonomicGroupTypes={[
TaxonomicFilterGroupType.Events,
TaxonomicFilterGroupType.Actions,
]}
propertiesTaxonomicGroupTypes={[
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.EventFeatureFlags,
TaxonomicFilterGroupType.Elements,
TaxonomicFilterGroupType.PersonProperties,
TaxonomicFilterGroupType.HogQLExpression,
...groupsTaxonomicTypes,
]}
propertyFiltersPopover
addFilterDefaultOptions={{
id: '$pageview',
name: '$pageview',
type: EntityTypes.EVENTS,
}}
buttonCopy="Add event matcher"
/>
</>
)}
{!useMapping ? (
<>
<div className="flex w-full gap-2 justify-between">
<LemonLabel>Match events and actions</LemonLabel>
</div>
<p className="mb-0 text-muted-alt text-xs">
If set, the destination will only run if the <b>event matches any</b> of the
below.
</p>
<ActionFilter
bordered
filters={value ?? {} /* TODO: this is any */}
setFilters={(payload) => {
onChange({
...value,
...sanitizeActionFilters(payload),
})
}}
typeKey="plugin-filters"
mathAvailability={MathAvailability.None}
hideRename
hideDuplicate
showNestedArrow={false}
actionsTaxonomicGroupTypes={[
TaxonomicFilterGroupType.Events,
TaxonomicFilterGroupType.Actions,
]}
propertiesTaxonomicGroupTypes={[
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.EventFeatureFlags,
TaxonomicFilterGroupType.Elements,
TaxonomicFilterGroupType.PersonProperties,
TaxonomicFilterGroupType.HogQLExpression,
...groupsTaxonomicTypes,
]}
propertyFiltersPopover
addFilterDefaultOptions={{
id: '$pageview',
name: '$pageview',
type: EntityTypes.EVENTS,
}}
buttonCopy="Add event matcher"
/>
</>
) : null}
</>
)
}}
</LemonField>

{showMasking ? (
<LemonField name="masking" label="Trigger options">
{({ value, onChange }) => (
Expand Down
Loading

0 comments on commit bae2b6f

Please sign in to comment.