diff --git a/static/app/views/detectors/components/detectorSearch.tsx b/static/app/views/detectors/components/detectorSearch.tsx index 7aef300a0968ca..a770161ceee4d1 100644 --- a/static/app/views/detectors/components/detectorSearch.tsx +++ b/static/app/views/detectors/components/detectorSearch.tsx @@ -1,57 +1,25 @@ import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder'; import {t} from 'sentry/locale'; -import type {TagCollection} from 'sentry/types/group'; -import type {FieldDefinition} from 'sentry/utils/fields'; -import {FieldKind} from 'sentry/utils/fields'; -import {DETECTOR_FILTER_KEYS} from 'sentry/views/detectors/constants'; +import {getFieldDefinition} from 'sentry/utils/fields'; +import {useDetectorFilterKeys} from 'sentry/views/detectors/utils/useDetectorFilterKeys'; type DetectorSearchProps = { initialQuery: string; onSearch: (query: string) => void; }; -function getDetectorFilterKeyDefinition(filterKey: string): FieldDefinition | null { - if (DETECTOR_FILTER_KEYS.hasOwnProperty(filterKey) && DETECTOR_FILTER_KEYS[filterKey]) { - const {description, valueType, keywords, values} = DETECTOR_FILTER_KEYS[filterKey]; - - return { - kind: FieldKind.FIELD, - desc: description, - valueType, - keywords, - values, - }; - } - - return null; -} - -const FILTER_KEYS: TagCollection = Object.fromEntries( - Object.keys(DETECTOR_FILTER_KEYS).map(key => { - const {values} = DETECTOR_FILTER_KEYS[key] ?? {}; - - return [ - key, - { - key, - name: key, - predefined: values !== undefined, - values, - }, - ]; - }) -); - export function DetectorSearch({initialQuery, onSearch}: DetectorSearchProps) { + const filterKeys = useDetectorFilterKeys(); + return ( Promise.resolve([])} searchSource="detectors-list" - fieldDefinitionGetter={getDetectorFilterKeyDefinition} + fieldDefinitionGetter={getFieldDefinition} disallowUnsupportedFilters disallowWildcard disallowLogicalOperators diff --git a/static/app/views/detectors/constants.tsx b/static/app/views/detectors/constants.tsx index 20a74db559dc55..09ee351edea0de 100644 --- a/static/app/views/detectors/constants.tsx +++ b/static/app/views/detectors/constants.tsx @@ -36,4 +36,9 @@ export const DETECTOR_FILTER_KEYS: Record< ] satisfies DetectorType[], keywords: ['type'], }, + assignee: { + description: 'User or team assigned to the monitor', + valueType: FieldValueType.STRING, + keywords: ['assigned', 'owner'], + }, }; diff --git a/static/app/views/detectors/list.spec.tsx b/static/app/views/detectors/list.spec.tsx index 8353a1cbf4eb9e..35dd494a53f15c 100644 --- a/static/app/views/detectors/list.spec.tsx +++ b/static/app/views/detectors/list.spec.tsx @@ -171,6 +171,31 @@ describe('DetectorsList', function () { expect(mockDetectorsRequestErrorType).toHaveBeenCalled(); }); + it('can filter by assignee', async function () { + const testUser = UserFixture({id: '2', email: 'test@example.com'}); + const mockDetectorsRequestAssignee = MockApiClient.addMockResponse({ + url: '/organizations/org-slug/detectors/', + body: [MetricDetectorFixture({name: 'Assigned Detector', owner: testUser.id})], + match: [MockApiClient.matchQuery({query: 'assignee:test@example.com'})], + }); + + render(, {organization}); + await screen.findByText('Detector 1'); + + // Click through menus to select assignee + const searchInput = await screen.findByRole('combobox', { + name: 'Add a search term', + }); + await userEvent.type(searchInput, 'assignee:test@example.com'); + + // It takes two enters. One to enter the search term, and one to submit the search. + await userEvent.keyboard('{enter}'); + await userEvent.keyboard('{enter}'); + + await screen.findByText('Assigned Detector'); + expect(mockDetectorsRequestAssignee).toHaveBeenCalled(); + }); + it('can sort the table', async function () { const mockDetectorsRequest = MockApiClient.addMockResponse({ url: '/organizations/org-slug/detectors/', diff --git a/static/app/views/detectors/utils/useDetectorFilterKeys.tsx b/static/app/views/detectors/utils/useDetectorFilterKeys.tsx new file mode 100644 index 00000000000000..b15e26dbddf75c --- /dev/null +++ b/static/app/views/detectors/utils/useDetectorFilterKeys.tsx @@ -0,0 +1,28 @@ +import {useMemo} from 'react'; + +import type {TagCollection} from 'sentry/types/group'; +import useAssignedSearchValues from 'sentry/utils/membersAndTeams/useAssignedSearchValues'; +import {DETECTOR_FILTER_KEYS} from 'sentry/views/detectors/constants'; + +export function useDetectorFilterKeys(): TagCollection { + const assignedValues = useAssignedSearchValues(); + + return useMemo(() => { + return Object.fromEntries( + Object.entries(DETECTOR_FILTER_KEYS).map(([key, config]) => { + const {values} = config ?? {}; + const isAssignee = key === 'assignee'; + + return [ + key, + { + key, + name: key, + predefined: isAssignee || values !== undefined, + values: isAssignee ? assignedValues : values, + }, + ]; + }) + ); + }, [assignedValues]); +}