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
92 changes: 72 additions & 20 deletions static/app/views/automations/components/actionNodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,47 @@ import {createContext, useContext} from 'react';
import {t} from 'sentry/locale';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {ActionType} from 'sentry/types/workflowEngine/actions';
import {AzureDevOpsNode} from 'sentry/views/automations/components/actions/azureDevOps';
import {DiscordNode} from 'sentry/views/automations/components/actions/discord';
import {EmailNode} from 'sentry/views/automations/components/actions/email';
import {GithubNode} from 'sentry/views/automations/components/actions/github';
import {GithubEnterpriseNode} from 'sentry/views/automations/components/actions/githubEnterprise';
import {JiraNode} from 'sentry/views/automations/components/actions/jira';
import {JiraServerNode} from 'sentry/views/automations/components/actions/jiraServer';
import {MSTeamsNode} from 'sentry/views/automations/components/actions/msTeams';
import {OpsgenieNode} from 'sentry/views/automations/components/actions/opsgenie';
import {PagerdutyNode} from 'sentry/views/automations/components/actions/pagerduty';
import {
AzureDevOpsDetails,
AzureDevOpsNode,
} from 'sentry/views/automations/components/actions/azureDevOps';
import {
DiscordDetails,
DiscordNode,
} from 'sentry/views/automations/components/actions/discord';
import {EmailDetails, EmailNode} from 'sentry/views/automations/components/actions/email';
import {
GithubDetails,
GithubNode,
} from 'sentry/views/automations/components/actions/github';
import {
GithubEnterpriseDetails,
GithubEnterpriseNode,
} from 'sentry/views/automations/components/actions/githubEnterprise';
import {JiraDetails, JiraNode} from 'sentry/views/automations/components/actions/jira';
import {
JiraServerDetails,
JiraServerNode,
} from 'sentry/views/automations/components/actions/jiraServer';
import {
MSTeamsDetails,
MSTeamsNode,
} from 'sentry/views/automations/components/actions/msTeams';
import {
OpsgenieDetails,
OpsgenieNode,
} from 'sentry/views/automations/components/actions/opsgenie';
import {
PagerdutyDetails,
PagerdutyNode,
} from 'sentry/views/automations/components/actions/pagerduty';
import {PluginNode} from 'sentry/views/automations/components/actions/plugin';
import {SentryAppNode} from 'sentry/views/automations/components/actions/sentryApp';
import {SlackNode} from 'sentry/views/automations/components/actions/slack';
import {WebhookNode} from 'sentry/views/automations/components/actions/webhook';
import {SlackDetails, SlackNode} from 'sentry/views/automations/components/actions/slack';
import {
WebhookDetails,
WebhookNode,
} from 'sentry/views/automations/components/actions/webhook';

interface ActionNodeProps {
action: Action;
Expand All @@ -43,42 +70,62 @@ type ActionNode = {
};

export const actionNodesMap = new Map<ActionType, ActionNode>([
[ActionType.AZURE_DEVOPS, {label: t('Azure DevOps'), action: AzureDevOpsNode}],
[ActionType.EMAIL, {label: t('Notify on preferred channel'), action: EmailNode}],
[
ActionType.AZURE_DEVOPS,
{label: t('Azure DevOps'), action: AzureDevOpsNode, details: AzureDevOpsDetails},
],
[
ActionType.EMAIL,
{label: t('Notify on preferred channel'), action: EmailNode, details: EmailDetails},
],
[
ActionType.DISCORD,
{
label: t('Discord'),
action: DiscordNode,
details: DiscordDetails,
},
],
[ActionType.GITHUB, {label: t('Github'), action: GithubNode}],
[ActionType.GITHUB, {label: t('Github'), action: GithubNode, details: GithubDetails}],
[
ActionType.GITHUB_ENTERPRISE,
{label: t('Github Enterprise'), action: GithubEnterpriseNode},
{
label: t('Github Enterprise'),
action: GithubEnterpriseNode,
details: GithubEnterpriseDetails,
},
],
[ActionType.JIRA, {label: t('Jira'), action: JiraNode, details: JiraDetails}],
[
ActionType.JIRA_SERVER,
{label: t('Jira Server'), action: JiraServerNode, details: JiraServerDetails},
],
[ActionType.JIRA, {label: t('Jira'), action: JiraNode}],
[ActionType.JIRA_SERVER, {label: t('Jira Server'), action: JiraServerNode}],
[
ActionType.MSTEAMS,
{
label: t('MS Teams'),
action: MSTeamsNode,
details: MSTeamsDetails,
},
],
[ActionType.OPSGENIE, {label: t('Opsgenie'), action: OpsgenieNode}],
[
ActionType.OPSGENIE,
{label: t('Opsgenie'), action: OpsgenieNode, details: OpsgenieDetails},
],
[
ActionType.PAGERDUTY,
{
label: t('Pagerduty'),
action: PagerdutyNode,
details: PagerdutyDetails,
},
],
[
ActionType.PLUGIN,
{
label: t('Legacy integrations'),
action: PluginNode,
details: PluginNode,
},
],
[
Expand All @@ -92,10 +139,15 @@ export const actionNodesMap = new Map<ActionType, ActionNode>([
{
label: t('Slack'),
action: SlackNode,
details: SlackDetails,
},
],
[
ActionType.WEBHOOK,
{label: t('Send a notification via an integration'), action: WebhookNode},
{
label: t('Send a notification via an integration'),
action: WebhookNode,
details: WebhookDetails,
},
],
]);
18 changes: 18 additions & 0 deletions static/app/views/automations/components/actions/azureDevOps.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import {ActionMetadata} from 'sentry/components/workflowEngine/ui/actionMetadata';
import {tct} from 'sentry/locale';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {ActionType} from 'sentry/types/workflowEngine/actions';
import {IntegrationField} from 'sentry/views/automations/components/actions/integrationField';
import {TicketActionSettingsButton} from 'sentry/views/automations/components/actions/ticketActionSettingsButton';

export function AzureDevOpsDetails({
action,
handler,
}: {
action: Action;
handler: ActionHandler;
}) {
const integrationName =
handler.integrations?.find(i => i.id === action.integrationId)?.name ||
action.integrationId;

return tct('Create an [logo] Azure DevOps work item in [integration]', {
logo: ActionMetadata[ActionType.AZURE_DEVOPS]?.icon,
integration: integrationName,
});
}

export function AzureDevOpsNode() {
return tct(
'Create an [logo] Azure DevOps work item in [integration] with these [settings]',
Expand Down
24 changes: 24 additions & 0 deletions static/app/views/automations/components/actions/discord.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,35 @@ import {BannerLink, InfoBanner} from 'sentry/components/workflowEngine/ui/infoBa
import {t, tct} from 'sentry/locale';
import {PluginIcon} from 'sentry/plugins/components/pluginIcon';
import {space} from 'sentry/styles/space';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {IntegrationField} from 'sentry/views/automations/components/actions/integrationField';
import {TagsField} from 'sentry/views/automations/components/actions/tagsField';
import {TargetDisplayField} from 'sentry/views/automations/components/actions/targetDisplayField';
import {ICON_SIZE} from 'sentry/views/automations/components/automationBuilderRow';

export function DiscordDetails({
action,
handler,
}: {
action: Action;
handler: ActionHandler;
}) {
const integrationName =
handler.integrations?.find(i => i.id === action.integrationId)?.name ||
action.integrationId;
const tags = String(action.data.tags);

return tct(
'Send a [logo] Discord message to [server] server, to channel with ID or URL [channel][tags]',
{
logo: <PluginIcon pluginId="discord" size={ICON_SIZE} />,
server: integrationName,
channel: String(action.config.target_identifier),
tags: action.data.tags ? `, and in the message show tags [${tags}]` : null,
}
);
}

export function DiscordNode() {
return (
<Flex column gap={space(1)} flex="1">
Expand Down
39 changes: 37 additions & 2 deletions static/app/views/automations/components/actions/email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import TeamSelector from 'sentry/components/teamSelector';
import AutomationBuilderSelectField, {
selectControlStyles,
} from 'sentry/components/workflowEngine/form/automationBuilderSelectField';
import {tct} from 'sentry/locale';
import {t, tct} from 'sentry/locale';
import type {Action} from 'sentry/types/workflowEngine/actions';
import {ActionTarget} from 'sentry/types/workflowEngine/actions';
import useOrganization from 'sentry/utils/useOrganization';
import {useTeamsById} from 'sentry/utils/useTeamsById';
import useUserFromId from 'sentry/utils/useUserFromId';
import {useActionNodeContext} from 'sentry/views/automations/components/actionNodes';

enum FallthroughChoiceType {
Expand All @@ -17,7 +20,7 @@ enum FallthroughChoiceType {
}

const TARGET_TYPE_CHOICES = [
{value: ActionTarget.ISSUE_OWNERS, label: 'Suggested assignees'},
{value: ActionTarget.ISSUE_OWNERS, label: 'Suggested Assignees'},
{value: ActionTarget.TEAM, label: 'Team'},
{value: ActionTarget.USER, label: 'Member'},
];
Expand All @@ -28,6 +31,38 @@ const FALLTHROUGH_CHOICES = [
{value: FallthroughChoiceType.NO_ONE, label: 'No One'},
];

export function EmailDetails({action}: {action: Action}) {
const {target_type, target_identifier} = action.config;

if (target_type === ActionTarget.ISSUE_OWNERS) {
return tct('Notify Suggested Assignees and, if none found, notify [fallthrough]', {
fallthrough:
FALLTHROUGH_CHOICES.find(choice => choice.value === action.data.fallthroughType)
?.label || String(action.data.fallthroughType),
});
}

if (target_type === ActionTarget.TEAM && target_identifier) {
return <AssignedToTeam teamId={target_identifier} />;
}
if (target_type === ActionTarget.USER && target_identifier) {
return <AssignedToMember memberId={parseInt(target_identifier, 10)} />;
}

return t('Notify on preferred channel');
}

function AssignedToTeam({teamId}: {teamId: string}) {
const {teams} = useTeamsById({ids: [teamId]});
const team = teams.find(tm => tm.id === teamId);
return t('Notify team %s', `#${team?.slug ?? 'unknown'}`);
}

function AssignedToMember({memberId}: {memberId: number}) {
const {data: user} = useUserFromId({id: memberId});
return t('Notify member %s', `${user?.email ?? 'unknown'}`);
}

export function EmailNode() {
return tct('Notify [targetType] [identifier]', {
targetType: <TargetTypeField />,
Expand Down
18 changes: 18 additions & 0 deletions static/app/views/automations/components/actions/github.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import {ActionMetadata} from 'sentry/components/workflowEngine/ui/actionMetadata';
import {tct} from 'sentry/locale';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {ActionType} from 'sentry/types/workflowEngine/actions';
import {IntegrationField} from 'sentry/views/automations/components/actions/integrationField';
import {TicketActionSettingsButton} from 'sentry/views/automations/components/actions/ticketActionSettingsButton';

export function GithubDetails({
action,
handler,
}: {
action: Action;
handler: ActionHandler;
}) {
const integrationName =
handler.integrations?.find(i => i.id === action.integrationId)?.name ||
action.integrationId;

return tct('Create a [logo] GitHub issue in [integration]', {
logo: ActionMetadata[ActionType.GITHUB]?.icon,
integration: integrationName,
});
}

export function GithubNode() {
return tct('Create a [logo] GitHub issue in [integration] with these [settings]', {
logo: ActionMetadata[ActionType.GITHUB]?.icon,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import {ActionMetadata} from 'sentry/components/workflowEngine/ui/actionMetadata';
import {tct} from 'sentry/locale';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {ActionType} from 'sentry/types/workflowEngine/actions';
import {IntegrationField} from 'sentry/views/automations/components/actions/integrationField';
import {TicketActionSettingsButton} from 'sentry/views/automations/components/actions/ticketActionSettingsButton';

export function GithubEnterpriseDetails({
action,
handler,
}: {
action: Action;
handler: ActionHandler;
}) {
const integrationName =
handler.integrations?.find(i => i.id === action.integrationId)?.name ||
action.integrationId;

return tct('Create a [logo] GitHub Enterprise issue in [integration]', {
logo: ActionMetadata[ActionType.GITHUB_ENTERPRISE]?.icon,
integration: integrationName,
});
}

export function GithubEnterpriseNode() {
return tct(
'Create a [logo] GitHub Enterprise issue in [integration] with these [settings]',
Expand Down
12 changes: 12 additions & 0 deletions static/app/views/automations/components/actions/jira.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import {ActionMetadata} from 'sentry/components/workflowEngine/ui/actionMetadata';
import {tct} from 'sentry/locale';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {ActionType} from 'sentry/types/workflowEngine/actions';
import {IntegrationField} from 'sentry/views/automations/components/actions/integrationField';
import {TicketActionSettingsButton} from 'sentry/views/automations/components/actions/ticketActionSettingsButton';

export function JiraDetails({action, handler}: {action: Action; handler: ActionHandler}) {
const integrationName =
handler.integrations?.find(i => i.id === action.integrationId)?.name ||
action.integrationId;

return tct('Create a [logo] Jira issue in [integration]', {
logo: ActionMetadata[ActionType.JIRA]?.icon,
integration: integrationName,
});
}

export function JiraNode() {
return tct('Create a [logo] Jira issue in [integration] with these [settings]', {
logo: ActionMetadata[ActionType.JIRA]?.icon,
Expand Down
18 changes: 18 additions & 0 deletions static/app/views/automations/components/actions/jiraServer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import {ActionMetadata} from 'sentry/components/workflowEngine/ui/actionMetadata';
import {tct} from 'sentry/locale';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import {ActionType} from 'sentry/types/workflowEngine/actions';
import {IntegrationField} from 'sentry/views/automations/components/actions/integrationField';
import {TicketActionSettingsButton} from 'sentry/views/automations/components/actions/ticketActionSettingsButton';

export function JiraServerDetails({
action,
handler,
}: {
action: Action;
handler: ActionHandler;
}) {
const integrationName =
handler.integrations?.find(i => i.id === action.integrationId)?.name ||
action.integrationId;

return tct('Create a [logo] Jira Server issue in [integration]', {
logo: ActionMetadata[ActionType.JIRA_SERVER]?.icon,
integration: integrationName,
});
}

export function JiraServerNode() {
return tct('Create a [logo] Jira Server issue in {integration} with these [settings]', {
logo: ActionMetadata[ActionType.JIRA_SERVER]?.icon,
Expand Down
Loading
Loading