|
@@ -1,4 +1,4 @@
|
|
-import {Fragment, PureComponent} from 'react';
|
|
|
|
|
|
+import {Fragment} from 'react';
|
|
import styled from '@emotion/styled';
|
|
import styled from '@emotion/styled';
|
|
|
|
|
|
import ActorAvatar from 'sentry/components/avatar/actorAvatar';
|
|
import ActorAvatar from 'sentry/components/avatar/actorAvatar';
|
|
@@ -12,58 +12,38 @@ import space from 'sentry/styles/space';
|
|
import {Actor, Member, Team} from 'sentry/types';
|
|
import {Actor, Member, Team} from 'sentry/types';
|
|
import {IssueAlertRule} from 'sentry/types/alerts';
|
|
import {IssueAlertRule} from 'sentry/types/alerts';
|
|
|
|
|
|
|
|
+import {TextAction, TextCondition} from './textRule';
|
|
|
|
+
|
|
type Props = {
|
|
type Props = {
|
|
memberList: Member[];
|
|
memberList: Member[];
|
|
rule: IssueAlertRule;
|
|
rule: IssueAlertRule;
|
|
teams: Team[];
|
|
teams: Team[];
|
|
};
|
|
};
|
|
|
|
|
|
-class Sidebar extends PureComponent<Props> {
|
|
|
|
- renderConditions() {
|
|
|
|
- const {rule, memberList, teams} = this.props;
|
|
|
|
-
|
|
|
|
- const conditions = rule.conditions.length
|
|
|
|
- ? rule.conditions.map(condition => (
|
|
|
|
- <ConditionsBadge key={condition.id}>{condition.name}</ConditionsBadge>
|
|
|
|
- ))
|
|
|
|
- : null;
|
|
|
|
- const filters = rule.filters.length
|
|
|
|
- ? rule.filters.map(filter => (
|
|
|
|
- <ConditionsBadge key={filter.id}>
|
|
|
|
- {filter.time ? filter.name + '(s)' : filter.name}
|
|
|
|
- </ConditionsBadge>
|
|
|
|
- ))
|
|
|
|
- : null;
|
|
|
|
- const actions = rule.actions.length ? (
|
|
|
|
- rule.actions.map(action => {
|
|
|
|
- let name = action.name;
|
|
|
|
- if (action.targetType === 'Member') {
|
|
|
|
- const user = memberList.find(
|
|
|
|
- member => member.user.id === `${action.targetIdentifier}`
|
|
|
|
- );
|
|
|
|
- name = t('Send a notification to %s', user?.email);
|
|
|
|
- }
|
|
|
|
- if (action.targetType === 'Team') {
|
|
|
|
- const team = teams.find(tm => tm.id === `${action.targetIdentifier}`);
|
|
|
|
- name = t('Send a notification to #%s', team?.name);
|
|
|
|
- }
|
|
|
|
- if (
|
|
|
|
- action.id === 'sentry.integrations.slack.notify_action.SlackNotifyServiceAction'
|
|
|
|
- ) {
|
|
|
|
- // Remove (optionally, an ID: XXX) from slack action
|
|
|
|
- name = name.replace(/\(optionally.*\)/, '');
|
|
|
|
- // Remove tags if they aren't used
|
|
|
|
- name = name.replace(' and show tags [] in notification', '');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return <ConditionsBadge key={action.id}>{name}</ConditionsBadge>;
|
|
|
|
- })
|
|
|
|
- ) : (
|
|
|
|
- <ConditionsBadge>{t('Do nothing')}</ConditionsBadge>
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- return (
|
|
|
|
- <PanelBody>
|
|
|
|
|
|
+function Conditions({rule, teams, memberList}: Props) {
|
|
|
|
+ return (
|
|
|
|
+ <PanelBody>
|
|
|
|
+ <Step>
|
|
|
|
+ <StepContainer>
|
|
|
|
+ <ChevronContainer>
|
|
|
|
+ <IconChevron color="gray200" isCircled direction="right" size="sm" />
|
|
|
|
+ </ChevronContainer>
|
|
|
|
+ <StepContent>
|
|
|
|
+ <StepLead>
|
|
|
|
+ {tct('[when:When] an event is captured [selector]', {
|
|
|
|
+ when: <Badge />,
|
|
|
|
+ selector: rule.conditions.length ? t('and %s...', rule.actionMatch) : '',
|
|
|
|
+ })}
|
|
|
|
+ </StepLead>
|
|
|
|
+ {rule.conditions.map((condition, idx) => (
|
|
|
|
+ <ConditionsBadge key={idx}>
|
|
|
|
+ <TextCondition condition={condition} />
|
|
|
|
+ </ConditionsBadge>
|
|
|
|
+ ))}
|
|
|
|
+ </StepContent>
|
|
|
|
+ </StepContainer>
|
|
|
|
+ </Step>
|
|
|
|
+ {rule.filters.length ? (
|
|
<Step>
|
|
<Step>
|
|
<StepContainer>
|
|
<StepContainer>
|
|
<ChevronContainer>
|
|
<ChevronContainer>
|
|
@@ -71,108 +51,102 @@ class Sidebar extends PureComponent<Props> {
|
|
</ChevronContainer>
|
|
</ChevronContainer>
|
|
<StepContent>
|
|
<StepContent>
|
|
<StepLead>
|
|
<StepLead>
|
|
- {tct('[when:When] an event is captured [selector]', {
|
|
|
|
- when: <Badge />,
|
|
|
|
- selector: conditions?.length ? t('and %s...', rule.actionMatch) : '',
|
|
|
|
|
|
+ {tct('[if:If] [selector] of these filters match', {
|
|
|
|
+ if: <Badge />,
|
|
|
|
+ selector: rule.filterMatch,
|
|
})}
|
|
})}
|
|
</StepLead>
|
|
</StepLead>
|
|
- {conditions}
|
|
|
|
|
|
+ {rule.filters.map((filter, idx) => (
|
|
|
|
+ <ConditionsBadge key={idx}>
|
|
|
|
+ {filter.time ? filter.name + '(s)' : filter.name}
|
|
|
|
+ </ConditionsBadge>
|
|
|
|
+ ))}
|
|
</StepContent>
|
|
</StepContent>
|
|
</StepContainer>
|
|
</StepContainer>
|
|
</Step>
|
|
</Step>
|
|
- {filters && (
|
|
|
|
- <Step>
|
|
|
|
- <StepContainer>
|
|
|
|
- <ChevronContainer>
|
|
|
|
- <IconChevron color="gray200" isCircled direction="right" size="sm" />
|
|
|
|
- </ChevronContainer>
|
|
|
|
- <StepContent>
|
|
|
|
- <StepLead>
|
|
|
|
- {tct('[if:If] [selector] of these filters match', {
|
|
|
|
- if: <Badge />,
|
|
|
|
- selector: rule.filterMatch,
|
|
|
|
- })}
|
|
|
|
- </StepLead>
|
|
|
|
- {filters}
|
|
|
|
- </StepContent>
|
|
|
|
- </StepContainer>
|
|
|
|
- </Step>
|
|
|
|
- )}
|
|
|
|
- <Step>
|
|
|
|
- <StepContainer>
|
|
|
|
- <ChevronContainer>
|
|
|
|
- <IconChevron isCircled color="gray200" direction="right" size="sm" />
|
|
|
|
- </ChevronContainer>
|
|
|
|
- <div>
|
|
|
|
- <StepLead>
|
|
|
|
- {tct('[then:Then] perform these actions', {
|
|
|
|
- then: <Badge />,
|
|
|
|
- })}
|
|
|
|
- </StepLead>
|
|
|
|
- {actions}
|
|
|
|
- </div>
|
|
|
|
- </StepContainer>
|
|
|
|
- </Step>
|
|
|
|
- </PanelBody>
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- render() {
|
|
|
|
- const {rule} = this.props;
|
|
|
|
-
|
|
|
|
- const ownerId = rule.owner?.split(':')[1];
|
|
|
|
- const teamActor = ownerId && {type: 'team' as Actor['type'], id: ownerId, name: ''};
|
|
|
|
|
|
+ ) : null}
|
|
|
|
+ <Step>
|
|
|
|
+ <StepContainer>
|
|
|
|
+ <ChevronContainer>
|
|
|
|
+ <IconChevron isCircled color="gray200" direction="right" size="sm" />
|
|
|
|
+ </ChevronContainer>
|
|
|
|
+ <div>
|
|
|
|
+ <StepLead>
|
|
|
|
+ {tct('[then:Then] perform these actions', {
|
|
|
|
+ then: <Badge />,
|
|
|
|
+ })}
|
|
|
|
+ </StepLead>
|
|
|
|
+ {rule.actions.length ? (
|
|
|
|
+ rule.actions.map((action, idx) => {
|
|
|
|
+ return (
|
|
|
|
+ <ConditionsBadge key={idx}>
|
|
|
|
+ <TextAction action={action} memberList={memberList} teams={teams} />
|
|
|
|
+ </ConditionsBadge>
|
|
|
|
+ );
|
|
|
|
+ })
|
|
|
|
+ ) : (
|
|
|
|
+ <ConditionsBadge>{t('Do nothing')}</ConditionsBadge>
|
|
|
|
+ )}
|
|
|
|
+ </div>
|
|
|
|
+ </StepContainer>
|
|
|
|
+ </Step>
|
|
|
|
+ </PanelBody>
|
|
|
|
+ );
|
|
|
|
+}
|
|
|
|
|
|
- return (
|
|
|
|
- <Fragment>
|
|
|
|
- <StatusContainer>
|
|
|
|
- <HeaderItem>
|
|
|
|
- <Heading noMargin>{t('Last Triggered')}</Heading>
|
|
|
|
- <Status>
|
|
|
|
- {rule.lastTriggered ? (
|
|
|
|
- <TimeSince date={rule.lastTriggered} />
|
|
|
|
- ) : (
|
|
|
|
- t('No alerts triggered')
|
|
|
|
- )}
|
|
|
|
- </Status>
|
|
|
|
- </HeaderItem>
|
|
|
|
- </StatusContainer>
|
|
|
|
- <SidebarGroup>
|
|
|
|
- <Heading noMargin>{t('Alert Conditions')}</Heading>
|
|
|
|
- {this.renderConditions()}
|
|
|
|
- </SidebarGroup>
|
|
|
|
- <SidebarGroup>
|
|
|
|
- <Heading>{t('Alert Rule Details')}</Heading>
|
|
|
|
- <KeyValueTable>
|
|
|
|
|
|
+function Sidebar({rule, teams, memberList}: Props) {
|
|
|
|
+ const ownerId = rule.owner?.split(':')[1];
|
|
|
|
+ const teamActor = ownerId && {type: 'team' as Actor['type'], id: ownerId, name: ''};
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <Fragment>
|
|
|
|
+ <StatusContainer>
|
|
|
|
+ <HeaderItem>
|
|
|
|
+ <Heading noMargin>{t('Last Triggered')}</Heading>
|
|
|
|
+ <Status>
|
|
|
|
+ {rule.lastTriggered ? (
|
|
|
|
+ <TimeSince date={rule.lastTriggered} />
|
|
|
|
+ ) : (
|
|
|
|
+ t('No alerts triggered')
|
|
|
|
+ )}
|
|
|
|
+ </Status>
|
|
|
|
+ </HeaderItem>
|
|
|
|
+ </StatusContainer>
|
|
|
|
+ <SidebarGroup>
|
|
|
|
+ <Heading noMargin>{t('Alert Conditions')}</Heading>
|
|
|
|
+ <Conditions rule={rule} teams={teams} memberList={memberList} />
|
|
|
|
+ </SidebarGroup>
|
|
|
|
+ <SidebarGroup>
|
|
|
|
+ <Heading>{t('Alert Rule Details')}</Heading>
|
|
|
|
+ <KeyValueTable>
|
|
|
|
+ <KeyValueTableRow
|
|
|
|
+ keyName={t('Environment')}
|
|
|
|
+ value={<OverflowTableValue>{rule.environment ?? '-'}</OverflowTableValue>}
|
|
|
|
+ />
|
|
|
|
+ {rule.dateCreated && (
|
|
<KeyValueTableRow
|
|
<KeyValueTableRow
|
|
- keyName={t('Environment')}
|
|
|
|
- value={<OverflowTableValue>{rule.environment ?? '-'}</OverflowTableValue>}
|
|
|
|
|
|
+ keyName={t('Date Created')}
|
|
|
|
+ value={<TimeSince date={rule.dateCreated} suffix={t('ago')} />}
|
|
/>
|
|
/>
|
|
- {rule.dateCreated && (
|
|
|
|
- <KeyValueTableRow
|
|
|
|
- keyName={t('Date Created')}
|
|
|
|
- value={<TimeSince date={rule.dateCreated} suffix={t('ago')} />}
|
|
|
|
- />
|
|
|
|
- )}
|
|
|
|
- {rule.createdBy && (
|
|
|
|
- <KeyValueTableRow
|
|
|
|
- keyName={t('Created By')}
|
|
|
|
- value={
|
|
|
|
- <OverflowTableValue>{rule.createdBy.name ?? '-'}</OverflowTableValue>
|
|
|
|
- }
|
|
|
|
- />
|
|
|
|
- )}
|
|
|
|
|
|
+ )}
|
|
|
|
+ {rule.createdBy && (
|
|
<KeyValueTableRow
|
|
<KeyValueTableRow
|
|
- keyName={t('Team')}
|
|
|
|
|
|
+ keyName={t('Created By')}
|
|
value={
|
|
value={
|
|
- teamActor ? <ActorAvatar actor={teamActor} size={24} /> : 'Unassigned'
|
|
|
|
|
|
+ <OverflowTableValue>{rule.createdBy.name ?? '-'}</OverflowTableValue>
|
|
}
|
|
}
|
|
/>
|
|
/>
|
|
- </KeyValueTable>
|
|
|
|
- </SidebarGroup>
|
|
|
|
- </Fragment>
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
|
|
+ )}
|
|
|
|
+ <KeyValueTableRow
|
|
|
|
+ keyName={t('Team')}
|
|
|
|
+ value={
|
|
|
|
+ teamActor ? <ActorAvatar actor={teamActor} size={24} /> : t('Unassigned')
|
|
|
|
+ }
|
|
|
|
+ />
|
|
|
|
+ </KeyValueTable>
|
|
|
|
+ </SidebarGroup>
|
|
|
|
+ </Fragment>
|
|
|
|
+ );
|
|
}
|
|
}
|
|
|
|
|
|
export default Sidebar;
|
|
export default Sidebar;
|