Skip to content

Commit

Permalink
[MM-47629] Don't crash when a run channel has been deleted (#1908)
Browse files Browse the repository at this point in the history
* don't crash when a run channel has been deleted

* i18n

* do not set channel

* Update webapp/src/components/backstage/playbook_runs/playbook_run/rhs_info_overview.tsx

Co-authored-by: Caleb Roseland <[email protected]>

---------

Co-authored-by: Caleb Roseland <[email protected]>
  • Loading branch information
JulienTant and calebroseland authored May 31, 2024
1 parent d7be0d4 commit 9f0e44c
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ describe('runs > run details page > run info', {testIsolation: true}, () => {
cy.url().should('include', `${testTeam.name}/channels/the-run-name`);
});
});

it('indicates when the channel has been deleted', () => {
cy.apiDeleteChannel(testRun.channel_id).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);

// * Assert channel name
getOverviewEntry('channel').contains('Channel deleted');
});
});
});

describe('as viewer', () => {
Expand Down
25 changes: 14 additions & 11 deletions server/app/playbook_run_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -1266,11 +1266,11 @@ func (s *PlaybookRunServiceImpl) GetPlaybookRunMetadata(playbookRunID string) (*
// Get main channel details
channel, err := s.pluginAPI.Channel.Get(playbookRun.ChannelID)
if err != nil {
return nil, errors.Wrapf(err, "failed to retrieve channel id '%s'", playbookRun.ChannelID)
s.pluginAPI.Log.Warn("failed to retrieve channel id", "channel_id", playbookRun.ChannelID)
}
team, err := s.pluginAPI.Team.Get(channel.TeamId)
team, err := s.pluginAPI.Team.Get(playbookRun.TeamID)
if err != nil {
return nil, errors.Wrapf(err, "failed to retrieve team id '%s'", channel.TeamId)
return nil, errors.Wrapf(err, "failed to retrieve team id '%s'", playbookRun.TeamID)
}

numParticipants, err := s.store.GetHistoricalPlaybookRunParticipantsCount(playbookRun.ChannelID)
Expand All @@ -1283,14 +1283,17 @@ func (s *PlaybookRunServiceImpl) GetPlaybookRunMetadata(playbookRunID string) (*
return nil, errors.Wrapf(err, "failed to get followers of playbook run %s", playbookRunID)
}

return &Metadata{
ChannelName: channel.Name,
ChannelDisplayName: channel.DisplayName,
TeamName: team.Name,
TotalPosts: channel.TotalMsgCount,
NumParticipants: numParticipants,
Followers: followers,
}, nil
metadata := &Metadata{
TeamName: team.Name,
NumParticipants: numParticipants,
Followers: followers,
}
if channel != nil {
metadata.ChannelName = channel.Name
metadata.ChannelDisplayName = channel.DisplayName
metadata.TotalPosts = channel.TotalMsgCount
}
return metadata, nil
}

// GetPlaybookRunsForChannelByUser get the playbookRuns list associated with this channel and user.
Expand Down
1 change: 1 addition & 0 deletions webapp/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"AG7PKJ": "Rename run",
"AML4RW": "Task assignments",
"AhY0vJ": "Leave and unfollow",
"AkyGP2": "Channel deleted",
"AoNLta": "There are no finished runs linked to this channel",
"Auj1ap": "Start a trial or upgrade your subscription.",
"B3Q5mz": "Trigger",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const PlaybookRunDetails = () => {
// we must force metadata refetch when participants change (leave&unfollow)
const [metadata, metadataResult] = useRunMetadata(playbookRun?.id, [JSON.stringify(playbookRun?.participant_ids)]);
const [statusUpdates] = useRunStatusUpdates(playbookRun?.id, [playbookRun?.status_posts.length]);
const [channel] = useChannel(playbookRun?.channel_id ?? '');
const [channel, channelResult] = useChannel(playbookRun?.channel_id ?? '');
const myUser = useSelector(getCurrentUser);
const {options, selectOption, eventsFilter, resetFilters} = useFilter();
const followState = useRunFollowers(metadata?.followers || []);
Expand Down Expand Up @@ -144,6 +144,8 @@ const PlaybookRunDetails = () => {
}
}, [urlHash]);

const channelDeleted = Boolean(channel?.delete_at) || channelResult.isErrorCode(404);

// not found or error
if (playbookRunResult.error !== null || metadataResult.error !== null) {
return <Redirect to={pluginErrorUrl(ErrorPageTypes.PLAYBOOK_RUNS)}/>;
Expand Down Expand Up @@ -176,6 +178,7 @@ const PlaybookRunDetails = () => {
role={role}
followState={followState}
channel={channel}
channelDeleted={channelDeleted}
onViewParticipants={() => RHS.open(RHSContent.RunParticipants, RHSParticipantsTitle, playbookRun.name, () => onViewInfo)}
onViewTimeline={() => RHS.open(RHSContent.RunTimeline, formatMessage({defaultMessage: 'Timeline'}), playbookRun.name, () => onViewInfo, false)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface Props {
runMetadata?: Metadata;
role: Role;
channel: Channel | undefined | null;
channelDeleted: boolean;
followState: FollowState;
onViewParticipants: () => void;
onViewTimeline: () => void;
Expand All @@ -42,6 +43,7 @@ const RHSInfo = (props: Props) => {
onViewParticipants={props.onViewParticipants}
editable={editable}
channel={props.channel}
channelDeleted={props.channelDeleted}
followState={props.followState}
playbook={props.playbook}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ interface Props {
runMetadata?: Metadata;
editable: boolean;
channel: Channel | undefined | null;
channelDeleted: boolean;
followState: FollowState;
playbook?: PlaybookWithChecklist;
role: Role;
Expand All @@ -91,7 +92,7 @@ const StyledArrowIcon = styled(ArrowForwardIosIcon)`
margin-left: 7px;
`;

const RHSInfoOverview = ({run, role, channel, runMetadata, followState, editable, playbook, onViewParticipants}: Props) => {
const RHSInfoOverview = ({run, role, channel, channelDeleted, runMetadata, followState, editable, playbook, onViewParticipants}: Props) => {
const {formatMessage} = useIntl();
const addToast = useToaster().add;
const refreshLHS = useLHSRefresh();
Expand Down Expand Up @@ -197,24 +198,13 @@ const RHSInfoOverview = ({run, role, channel, runMetadata, followState, editable
icon={ProductChannelsIcon}
name={formatMessage({defaultMessage: 'Channel'})}
>
{channel && runMetadata ? <>
<ItemLink
to={`/${runMetadata.team_name}/channels/${channel.name}`}
data-testid='runinfo-channel-link'
>
<ItemContent >
{channel.display_name}
</ItemContent>
<OpenInNewIcon
size={14}
color={'var(--button-bg)'}
/>
</ItemLink>
</> : <ItemDisabledContent>
{role === Role.Participant ? <RequestJoinButton onClick={showRequestJoinConfirm}>{formatMessage({defaultMessage: 'Request to Join'})}</RequestJoinButton> : null}
<LockOutlineIcon size={20}/> {formatMessage({defaultMessage: 'Private'})}
</ItemDisabledContent>
}
<ChannelRow
channel={channel}
runMetadata={runMetadata}
channelDeleted={channelDeleted}
role={role}
onClickRequestJoin={showRequestJoinConfirm}
/>
</Item>
{RequestJoinModal}
</Section>
Expand Down Expand Up @@ -277,7 +267,7 @@ const ItemDisabledContent = styled(ItemContent)`
color: rgba(var(--center-channel-color-rgb), 0.64);
`;

const OverviewRow = styled.div<{onClick?: () => void}>`
const OverviewRow = styled.div<{ onClick?: () => void }>`
padding: 10px 24px;
height: 44px;
display: flex;
Expand Down Expand Up @@ -321,3 +311,47 @@ const ParticipantsContainer = styled.div`
flex-direction: row;
align-items: center;
`;

interface ChannelRowProps {
channel: Channel | undefined | null;
channelDeleted: boolean;
runMetadata?: Metadata;
role: Role;
onClickRequestJoin: () => void;
}

const ChannelRow = ({channel, runMetadata, channelDeleted, role, onClickRequestJoin}: ChannelRowProps) => {
const {formatMessage} = useIntl();

if (channelDeleted) {
return (
<ItemDisabledContent>
{formatMessage({defaultMessage: 'Channel deleted'})}
</ItemDisabledContent>
);
}

if (channel && runMetadata) {
return (
<ItemLink
to={`/${runMetadata.team_name}/channels/${channel.name}`}
data-testid='runinfo-channel-link'
>
<ItemContent>
{channel.display_name}
</ItemContent>
<OpenInNewIcon
size={14}
color={'var(--button-bg)'}
/>
</ItemLink>
);
}

return (
<ItemDisabledContent>
{role === Role.Participant ? <RequestJoinButton onClick={onClickRequestJoin}>{formatMessage({defaultMessage: 'Request to Join'})}</RequestJoinButton> : null}
<LockOutlineIcon size={20}/> {formatMessage({defaultMessage: 'Private'})}
</ItemDisabledContent>
);
};

0 comments on commit 9f0e44c

Please sign in to comment.