Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Activated Alert Rules to alert rule index #69124

Merged
merged 10 commits into from
Apr 18, 2024
Merged
Prev Previous commit
Next Next commit
updated row status badge
  • Loading branch information
nhsiehgit committed Apr 18, 2024
commit 5edcf36927399b3f521fda85fc829d4ad255eae9
26 changes: 23 additions & 3 deletions static/app/components/badge/alertBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import styled from '@emotion/styled';

import {DiamondStatus} from 'sentry/components/diamondStatus';
import {IconCheckmark, IconExclamation, IconFire, IconIssues} from 'sentry/icons';
import {
IconCheckmark,
IconEllipsis,
IconExclamation,
IconFire,
IconHappy,
IconIssues,
} from 'sentry/icons';
import type {SVGIconProps} from 'sentry/icons/svgIcon';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {ColorOrAlias} from 'sentry/utils/theme';
import {IncidentStatus} from 'sentry/views/alerts/types';
import {ActivationStatus, IncidentStatus} from 'sentry/views/alerts/types';

type Props = {
/**
* The rule is actively monitoring
*/
activationStatus?: ActivationStatus;
/**
* @deprecated use withText
*/
Expand All @@ -31,7 +42,7 @@ type Props = {
* This badge is a composition of DiamondStatus specifically used for incident
* alerts.
*/
function AlertBadge({status, withText, isIssue}: Props) {
function AlertBadge({status, withText, isIssue, activationStatus}: Props) {
let statusText = t('Resolved');
let Icon: React.ComponentType<SVGIconProps> = IconCheckmark;
let color: ColorOrAlias = 'successText';
Expand All @@ -50,6 +61,15 @@ function AlertBadge({status, withText, isIssue}: Props) {
color = 'warningText';
}

if (activationStatus === ActivationStatus.WAITING) {
statusText = t('Ready');
Icon = IconHappy;
color = 'purple300';
} else if (activationStatus === ActivationStatus.MONITORING) {
statusText = t('Monitoring');
Icon = IconEllipsis;
}

return (
<Wrapper data-test-id="alert-badge">
<DiamondStatus
Expand Down
29 changes: 15 additions & 14 deletions static/app/views/alerts/list/rules/activatedRuleRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useState} from 'react';
import {useMemo, useState} from 'react';
import styled from '@emotion/styled';

import Access from 'sentry/components/acl/access';
Expand Down Expand Up @@ -32,7 +32,7 @@ import {
} from 'sentry/views/alerts/rules/metric/types';

import type {CombinedMetricIssueAlerts, MetricAlert} from '../../types';
import {CombinedAlertType, IncidentStatus} from '../../types';
import {ActivationStatus, CombinedAlertType, IncidentStatus} from '../../types';

type Props = {
hasEditAccess: boolean;
Expand All @@ -59,6 +59,12 @@ function ActivatedRuleListRow({
}: Props) {
const {teams: userTeams} = useUserTeams();
const [assignee, setAssignee] = useState<string>('');
const isWaiting = useMemo(
() =>
!rule.activations?.length ||
(rule.activations?.length && rule.activations[0].isComplete),
[rule]
);

function renderLatestActivation(): React.ReactNode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: recommend decomposing each of the render calls. If you're accessing things like the rule it'd be extra cool to use react-query to avoid prop drilling too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm how does react-query addres prop drilling?
Or did you mean context?

for now i think i'll keep these patterns to match rules.tsx 😅
I'll create a follow up ticket to clean both of these up though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react-query uses context to store api data for you. (it also handles automatic updates and syncing data across all uses of the "store").

for example, if you wanted to fetch a specific rule you could do useRule(ruleId) -- then that hook uses the api react-query hook to fetch the data (which updates the value in the store for rule etc).


If you haven't used it before.. it's kinda a mind meld and we should def wait until later to update. i'm planning on using it for the API requests with project settings pages, so we could also take a look at it when that comes up.

if (!rule.activations?.length) {
Expand All @@ -83,8 +89,6 @@ function ActivatedRuleListRow({
}

function renderAlertRuleStatus(): React.ReactNode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend decomposing this quite a bit.. you could really simplify this by pulling the alert status out as a different component.. then applying SOLID programming principles split this into it's most basic pieces.. each alert threshold state. Then have the AlertRuleStatus simply look at the state and decide which component to render. That would simplify the UI components, decouple your logic from presentation, and simplify testing.

// const isActive = !(rule.activations?.length && rule.activations[0].isComplete);

if (rule.snooze) {
return renderSnoozeStatus();
}
Expand Down Expand Up @@ -160,13 +164,6 @@ function ActivatedRuleListRow({

const canEdit = ownerId ? userTeams.some(team => team.id === ownerId) : true;

const IssueStatusText: Record<IncidentStatus, string> = {
[IncidentStatus.CRITICAL]: t('Critical'),
[IncidentStatus.WARNING]: t('Warning'),
[IncidentStatus.CLOSED]: t('Resolved'),
[IncidentStatus.OPENED]: t('Resolved'),
};

const actions: MenuItemProps[] = [
{
key: 'edit',
Expand Down Expand Up @@ -264,11 +261,15 @@ function ActivatedRuleListRow({
<FlexCenter>
<Tooltip
title={tct('Metric Alert Status: [status]', {
status:
IssueStatusText[rule?.latestIncident?.status ?? IncidentStatus.CLOSED],
status: isWaiting ? 'Ready to monitor' : 'Monitoring',
})}
>
<AlertBadge status={rule?.latestIncident?.status} />
<AlertBadge
status={rule?.latestIncident?.status}
activationStatus={
isWaiting ? ActivationStatus.WAITING : ActivationStatus.MONITORING
}
/>
</Tooltip>
</FlexCenter>
<AlertNameAndStatus>
Expand Down
5 changes: 5 additions & 0 deletions static/app/views/alerts/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ export enum IncidentStatus {
CRITICAL = 20,
}

export enum ActivationStatus {
WAITING = 0,
MONITORING = 1,
}

export enum IncidentStatusMethod {
MANUAL = 1,
RULE_UPDATED = 2,
Expand Down