Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions frontend/common/services/useWarehouseConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ export const warehouseConnectionService = service
url: `environments/${environmentId}/warehouse-connections/${id}/`,
}),
}),
getWarehouseConnectionEvents: builder.query<
Res['warehouseConnectionEvents'],
Req['getWarehouseConnectionEvents']
>({
providesTags: [{ id: 'EVENTS', type: 'WarehouseConnection' }],
query: ({ environmentId, id }) => ({
url: `environments/${environmentId}/warehouse-connections/${id}/events/`,
}),
}),
Comment thread
Zaimwa9 marked this conversation as resolved.
getWarehouseConnections: builder.query<
Res['warehouseConnections'],
Req['getWarehouseConnections']
Expand Down Expand Up @@ -75,6 +84,7 @@ export const warehouseConnectionService = service
export const {
useCreateWarehouseConnectionMutation,
useDeleteWarehouseConnectionMutation,
useGetWarehouseConnectionEventsQuery,
useGetWarehouseConnectionsQuery,
useTestWarehouseConnectionConfigMutation,
useTestWarehouseConnectionMutation,
Expand Down
1 change: 1 addition & 0 deletions frontend/common/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,7 @@ export type Req = {
environmentId: string
exclude_event_stats?: boolean
}
getWarehouseConnectionEvents: { environmentId: string; id: number }
createWarehouseConnection: {
environmentId: string
warehouse_type: string
Expand Down
6 changes: 6 additions & 0 deletions frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,11 @@ export type WarehouseConnectionTestResult = {
status_detail: string | null
}

export type WarehouseConnectionEvents = {
events: string[]
is_truncated: boolean
}

export type WarehouseConnection = {
id: number
warehouse_type: WarehouseType
Expand Down Expand Up @@ -1523,6 +1528,7 @@ export type Res = {
gitlabIssues: PagedResponse<GitLabIssue>
gitlabMergeRequests: PagedResponse<GitLabMergeRequest>
warehouseConnections: WarehouseConnection[]
warehouseConnectionEvents: WarehouseConnectionEvents
warehouseConnectionTestResult: WarehouseConnectionTestResult
experiments: PagedResponse<Experiment> & {
currentPage: number
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { FC, useState } from 'react'
import Button from 'components/base/forms/Button'
import Input from 'components/base/forms/Input'
import EventNameSelect from 'components/experiments/EventNameSelect'
import {
canSubmitMetric,
DEFAULT_METRIC_FORM_STATE,
Expand Down Expand Up @@ -149,13 +150,9 @@ const CreateMetricForm: FC<CreateMetricFormProps> = ({
>
Event name
</label>
<Input
id='metric-event'
<EventNameSelect
value={state.event}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
update({ event: e.target.value })
}
placeholder='e.g. checkout_completed'
onChange={(event) => update({ event })}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.event-name-select {
position: relative;

.react-select__menu {
position: absolute;
left: 0;
right: 0;
z-index: 3;
}

.react-select .react-select__menu-list {
overflow-y: auto;
scrollbar-width: auto;

&::-webkit-scrollbar {
display: block;
width: 8px;
}

&::-webkit-scrollbar-track {
background: transparent;
}

&::-webkit-scrollbar-thumb {
background-color: var(--color-border-strong);
border-radius: 4px;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { FC, useMemo } from 'react'
import CreatableSelect from 'react-select/creatable'
import {
useGetWarehouseConnectionEventsQuery,
useGetWarehouseConnectionsQuery,
} from 'common/services/useWarehouseConnection'
import { WarehouseType } from 'common/types/responses'
import { useRouteContext } from 'components/providers/RouteContext'
import Icon from 'components/icons/Icon'
import { buildEventOptions, EventOption, isUnknownEvent } from './utils'
import './EventNameSelect.scss'

type EventNameSelectProps = {
value: string
onChange: (value: string) => void
}

const SUPPORTED_WAREHOUSE_TYPES: WarehouseType[] = ['flagsmith', 'clickhouse']

const EventNameSelect: FC<EventNameSelectProps> = ({ onChange, value }) => {
const { environmentId } = useRouteContext()
const { data: connections } = useGetWarehouseConnectionsQuery(
{ environmentId: environmentId ?? '', exclude_event_stats: true },
{ skip: !environmentId },
)
const connection = connections?.[0]
const canListEvents =
!!connection &&
SUPPORTED_WAREHOUSE_TYPES.includes(connection.warehouse_type)
const { data, isLoading, isSuccess } = useGetWarehouseConnectionEventsQuery(
{ environmentId: environmentId ?? '', id: connection?.id ?? 0 },
{ skip: !environmentId || !canListEvents },
)
const options = useMemo(() => buildEventOptions(data?.events), [data?.events])
const showWarning = isSuccess && isUnknownEvent(value, data?.events)
Comment thread
Zaimwa9 marked this conversation as resolved.

return (
<div className='event-name-select'>
<CreatableSelect
inputId='metric-event'
className='react-select'
classNamePrefix='react-select'
isClearable
isLoading={isLoading}
maxMenuHeight={200}
menuPlacement='auto'
options={options}
value={value ? { label: value, value } : null}
onChange={(option: EventOption | null) => onChange(option?.value ?? '')}
placeholder='e.g. checkout_completed'
formatCreateLabel={(input: string) => `Use "${input}"`}
noOptionsMessage={() => 'Type to add a new event'}
/>
{showWarning && (
<span className='d-flex align-items-center gap-1 text-warning fs-small mt-1'>
<Icon name='warning' width={14} className='text-warning' />
Comment thread
Zaimwa9 marked this conversation as resolved.
This event hasn&apos;t been received by your warehouse yet.
</span>
)}
</div>
)
}

EventNameSelect.displayName = 'EventNameSelect'
export default EventNameSelect
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
buildEventOptions,
isUnknownEvent,
} from 'components/experiments/EventNameSelect/utils'

describe('buildEventOptions', () => {
it.each([
['undefined', undefined, []],
['empty list', [], []],
[
'events',
['checkout_completed', 'page_view'],
[
{ label: 'checkout_completed', value: 'checkout_completed' },
{ label: 'page_view', value: 'page_view' },
],
],
])('%s → options', (_, events, expected) => {
expect(buildEventOptions(events)).toEqual(expected)
})
})

describe('isUnknownEvent', () => {
it.each([
['empty value', '', ['page_view'], false],
['known value', 'page_view', ['page_view'], false],
['unknown value', 'checkout', ['page_view'], true],
['no fetched list', 'checkout', undefined, false],
['empty fetched list', 'checkout', [], true],
])('%s', (_, value, events, expected) => {
expect(isUnknownEvent(value, events)).toBe(expected)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './EventNameSelect'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type EventOption = { label: string; value: string }

export const buildEventOptions = (events?: string[]): EventOption[] =>
(events ?? []).map((event) => ({ label: event, value: event }))

export const isUnknownEvent = (value: string, events?: string[]): boolean =>
!!value && Array.isArray(events) && !events.includes(value)
Loading