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
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('DetectorDetailsAutomations', () => {
const issueStreamDetector = IssueStreamDetectorFixture({id: 'issue-stream-1'});

beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
MockApiClient.clearMockResponses();

MockApiClient.addMockResponse({
Expand Down Expand Up @@ -98,6 +98,47 @@ describe('DetectorDetailsAutomations', () => {
);
});

it('can search connected alerts', async () => {
const automation2 = AutomationFixture({id: '2', name: 'Alert 2'});
const detector = UptimeDetectorFixture({
workflowIds: [automation1.id, automation2.id],
});

MockApiClient.addMockResponse({
url: '/organizations/org-slug/workflows/',
method: 'GET',
body: [automation1, automation2],
match: [
(_url, options) =>
options.query?.detector !== undefined && options.query?.query === undefined,
],
});

const searchRequest = MockApiClient.addMockResponse({
url: '/organizations/org-slug/workflows/',
method: 'GET',
body: [automation2],
match: [
(_url, options) =>
options.query?.detector !== undefined &&
typeof options.query?.query === 'string' &&
options.query.query.includes('Alert 2'),
],
});

render(<DetectorDetailsAutomations detector={detector} />, {organization});

expect(await screen.findByText(automation1.name)).toBeInTheDocument();

const searchInput = screen.getByRole('combobox', {name: 'Add a search term'});
await userEvent.click(searchInput);
await userEvent.keyboard('Alert 2{Enter}');

await waitFor(() => expect(searchRequest).toHaveBeenCalled());
expect(await screen.findByRole('link', {name: automation2.name})).toBeInTheDocument();
expect(screen.queryByRole('link', {name: automation1.name})).not.toBeInTheDocument();
});

it('can connect a new automation from drawer', async () => {
const detector = UptimeDetectorFixture({
id: 'detector-123',
Expand Down
126 changes: 68 additions & 58 deletions static/app/views/detectors/components/details/common/automations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import AutomationTitleCell from 'sentry/components/workflowEngine/gridCell/autom
import Section from 'sentry/components/workflowEngine/ui/section';
import {IconAdd} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Detector} from 'sentry/types/workflowEngine/detectors';
import {defined} from 'sentry/utils';
import {parseCursor} from 'sentry/utils/cursor';
import useOrganization from 'sentry/utils/useOrganization';
import useProjectFromId from 'sentry/utils/useProjectFromId';
import {AutomationSearch} from 'sentry/views/automations/components/automationListTable/search';
import {useAutomationsQuery} from 'sentry/views/automations/hooks';
import {getAutomationActions} from 'sentry/views/automations/hooks/utils';
import {makeAutomationCreatePathname} from 'sentry/views/automations/pathnames';
Expand Down Expand Up @@ -81,6 +81,11 @@ function DetectorAutomationsTable({
const issueStreamDetectorId = issueStreamDetectors?.[0]?.id;
const detectorIds = [detectorId, issueStreamDetectorId];
const [cursor, setCursor] = useState<string | undefined>(undefined);
const [searchQuery, setSearchQuery] = useState('');
const onSearch = useCallback((query: string) => {
setSearchQuery(query);
setCursor(undefined);
}, []);

const {
data: automations,
Expand All @@ -93,6 +98,7 @@ function DetectorAutomationsTable({
detector: detectorIds.filter(defined),
limit: AUTOMATIONS_PER_PAGE,
cursor,
query: searchQuery || undefined,
},
{enabled: !issueStreamDetectorsPending}
);
Expand Down Expand Up @@ -126,62 +132,67 @@ function DetectorAutomationsTable({
onRetry={refetchIssueStreamDetectors}
/>
)}
<SimpleTableWithColumns>
<SimpleTable.Header>
<SimpleTable.HeaderCell>{t('Name')}</SimpleTable.HeaderCell>
<SimpleTable.HeaderCell data-column-name="action-filters">
{t('Actions')}
</SimpleTable.HeaderCell>
<SimpleTable.HeaderCell data-column-name="triggered-by">
{t('Triggered By Issues')}
</SimpleTable.HeaderCell>
</SimpleTable.Header>
{isPending && <Skeletons numberOfRows={AUTOMATIONS_PER_PAGE} />}
{isError && <LoadingError />}
{isSuccess && automations.length === 0 && (
<SimpleTable.Empty>{emptyMessage}</SimpleTable.Empty>
)}
{isSuccess &&
automations.map(automation => (
<SimpleTable.Row
key={automation.id}
variant={automation.enabled ? 'default' : 'faded'}
>
<SimpleTable.RowCell>
<AutomationTitleCell automation={automation} />
</SimpleTable.RowCell>
<SimpleTable.RowCell data-column-name="action-filters">
<ActionCell actions={getAutomationActions(automation)} />
</SimpleTable.RowCell>
<SimpleTable.RowCell data-column-name="triggered-by">
{automation.detectorIds.includes(detectorId) ? (
<Tooltip
title={t('This Alert is directly connected to the current monitor.')}
showUnderline
>
{t('From Monitor')}
</Tooltip>
) : (
<Tooltip
title={tct(
'This Alert can be triggered by all issues in [projectName].',
{
projectName: project ? (
<strong>{project.slug}</strong>
) : (
'project'
),
}
)}
showUnderline
>
{t('In Project')}
</Tooltip>
)}
</SimpleTable.RowCell>
</SimpleTable.Row>
))}
</SimpleTableWithColumns>
<Stack gap="md">
<AutomationSearch initialQuery={searchQuery} onSearch={onSearch} />
<SimpleTableWithColumns>
<SimpleTable.Header>
<SimpleTable.HeaderCell>{t('Name')}</SimpleTable.HeaderCell>
<SimpleTable.HeaderCell data-column-name="action-filters">
{t('Actions')}
</SimpleTable.HeaderCell>
<SimpleTable.HeaderCell data-column-name="triggered-by">
{t('Triggered By Issues')}
</SimpleTable.HeaderCell>
</SimpleTable.Header>
{isPending && <Skeletons numberOfRows={AUTOMATIONS_PER_PAGE} />}
{isError && <LoadingError />}
{isSuccess && automations.length === 0 && (
<SimpleTable.Empty>{emptyMessage}</SimpleTable.Empty>
)}
{isSuccess &&
automations.map(automation => (
<SimpleTable.Row
key={automation.id}
variant={automation.enabled ? 'default' : 'faded'}
>
<SimpleTable.RowCell>
<AutomationTitleCell automation={automation} />
</SimpleTable.RowCell>
<SimpleTable.RowCell data-column-name="action-filters">
<ActionCell actions={getAutomationActions(automation)} />
</SimpleTable.RowCell>
<SimpleTable.RowCell data-column-name="triggered-by">
{automation.detectorIds.includes(detectorId) ? (
<Tooltip
title={t(
'This Alert is directly connected to the current monitor.'
)}
showUnderline
>
{t('From Monitor')}
</Tooltip>
) : (
<Tooltip
title={tct(
'This Alert can be triggered by all issues in [projectName].',
{
projectName: project ? (
<strong>{project.slug}</strong>
) : (
'project'
),
}
)}
showUnderline
>
{t('In Project')}
</Tooltip>
)}
</SimpleTable.RowCell>
</SimpleTable.Row>
))}
</SimpleTableWithColumns>
</Stack>
<Pagination
onCursor={setCursor}
pageLinks={pageLinks}
Expand Down Expand Up @@ -306,7 +317,6 @@ const Container = styled('div')`

const SimpleTableWithColumns = styled(SimpleTable)`
grid-template-columns: 1fr 180px auto;
margin-bottom: ${space(2)};

@container (max-width: ${p => p.theme.breakpoints.sm}) {
grid-template-columns: 1fr 180px;
Expand Down
Loading