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
54 changes: 35 additions & 19 deletions web-server/pages/api/internal/team/[team_id]/get_incidents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { endOfDay, startOfDay } from 'date-fns';
import { isNil, reject } from 'ramda';
import * as yup from 'yup';

import { getTeamPrs } from '@/api/internal/team/[team_id]/insights';
import { getTeamRevertedPrs } from '@/api/internal/team/[team_id]/revert_prs';
import { getAllTeamsReposProdBranchesForOrgAsMap } from '@/api/internal/team/[team_id]/repo_branches';
import { handleRequest } from '@/api-helpers/axios';
import { Endpoint } from '@/api-helpers/global';
import { updatePrFilterParams } from '@/api-helpers/team';
import {
updatePrFilterParams,
repoFiltersFromTeamProdBranches
} from '@/api-helpers/team';
import { mockDeploymentsWithIncidents } from '@/mocks/incidents';
import {
DeploymentWithIncidents,
Expand All @@ -22,7 +24,8 @@ const getSchema = yup.object().shape({
from_date: yup.date().required(),
to_date: yup.date().required(),
branches: yup.string().optional().nullable(),
repo_filters: yup.mixed().optional().nullable()
repo_filters: yup.mixed().optional().nullable(),
org_id: yup.string().uuid().required()
});

const endpoint = new Endpoint(pathSchema);
Expand All @@ -36,7 +39,8 @@ endpoint.handle.GET(getSchema, async (req, res) => {
from_date: rawFromDate,
to_date: rawToDate,
branches,
repo_filters
repo_filters,
org_id
} = req.payload;
const from_date = startOfDay(new Date(rawFromDate));
const to_date = endOfDay(new Date(rawToDate));
Expand All @@ -50,29 +54,40 @@ endpoint.handle.GET(getSchema, async (req, res) => {
{ branches, repo_filters }
);

const [deploymentsWithIncident, summaryPrs, revertedPrs] = await Promise.all([
getTeamIncidentsWithDeployment({ team_id, from_date, to_date }),
getTeamPrs({
team_id,
branches,
from_date: from_date,
to_date: to_date,
repo_filters
}).then((r) => r.data),
getTeamRevertedPrs({ ...params, team_id })
]);
const teamProdBranchesMap =
await getAllTeamsReposProdBranchesForOrgAsMap(org_id);
const teamRepoFiltersMap =
repoFiltersFromTeamProdBranches(teamProdBranchesMap);
const pr_filter = await updatePrFilterParams(
team_id,
{},
{
branches: branches,
repo_filters: !branches ? teamRepoFiltersMap[team_id] : null
}
).then(({ pr_filter }) => ({
pr_filter
}));

const deploymentsWithIncident = await getTeamIncidentsWithDeployment({
team_id,
from_date,
to_date,
pr_filter
});

return res.send({
deployments_with_incidents: deploymentsWithIncident,
summary_prs: summaryPrs,
revert_prs: revertedPrs
summary_prs: [],
revert_prs: []
} as IncidentApiResponseType);
});

export const getTeamIncidentsWithDeployment = async (params: {
team_id: ID;
from_date: DateString | Date;
to_date: DateString | Date;
pr_filter: any;
}) => {
const [from_time, to_time] = getWeekStartAndEndInterval(
params.from_date,
Expand All @@ -84,7 +99,8 @@ export const getTeamIncidentsWithDeployment = async (params: {
{
params: reject(isNil, {
from_time,
to_time
to_time,
pr_filter: params.pr_filter
})
}
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { FlexBox } from '../FlexBox';

export const ChangeFailureRate = () => {
return <FlexBox>ChangeFailureRate</FlexBox>;
};
56 changes: 8 additions & 48 deletions web-server/src/components/PRTable/PullRequestsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ import { PullRequestTableColumnSelector } from '@/components/PRTable/PullRequest
import { DarkTooltip, LightTooltip } from '@/components/Shared';
import { Line } from '@/components/Text';
import { SearchInput } from '@/components/TicketsTableAddons/SearchInput';
import { Integration } from '@/constants/integrations';
import { ROUTES } from '@/constants/routes';
import { useAuth } from '@/hooks/useAuth';
import { EasyState, useEasyState } from '@/hooks/useEasyState';
import { useTableSort } from '@/hooks/useTableSort';
import { DEFAULT_PR_TABLE_COLUMN_STATE_MAP } from '@/slices/app';
Expand Down Expand Up @@ -65,8 +62,6 @@ export const PullRequestsTable: FC<
} & Omit<PullRequestsTableHeadProps, 'conf' | 'updateSortConf' | 'count'>
> = ({ propPrs, selectionMenu, selectedPrIds, isPrSelectionEnabled }) => {
const theme = useTheme();
const { integrationSet } = useAuth();
const hasBitbucket = integrationSet.has(Integration.BITBUCKET);
const prTableColumnConfig = useSelector(
(s) => s.app.prTableColumnsConfig || DEFAULT_PR_TABLE_COLUMN_STATE_MAP
);
Expand Down Expand Up @@ -113,7 +108,6 @@ export const PullRequestsTable: FC<
}
}, [page, pageCount]);

const { orgId } = useAuth();
const enableCsv = true;

return (
Expand Down Expand Up @@ -306,12 +300,7 @@ export const PullRequestsTable: FC<
arrow
title={
<Box>
<Box>
{hasBitbucket
? pr.author.linked_user?.name ||
pr.author.username
: `@${pr.author.username}`}
</Box>
<Box>{`@${pr.author.username}`}</Box>
{!pr.author.linked_user && (
<Box fontStyle="italic" color="secondary.dark">
User not added to Middleware
Expand All @@ -327,18 +316,8 @@ export const PullRequestsTable: FC<
width="100%"
>
<Box
component={hasBitbucket ? 'div' : Link}
href={
pr.author.linked_user?.id
? `${
ROUTES.COLLABORATE.INSIGHTS.USER.add(
pr.author.linked_user.id
).PATH
}`
: hasBitbucket
? undefined
: getGHAvatar(pr.author.username)
}
component={Link}
href={getGHAvatar(pr.author.username)}
target="_blank"
fontWeight={500}
display="flex"
Expand All @@ -355,11 +334,7 @@ export const PullRequestsTable: FC<
boxShadow: `0 0 0 2px ${theme.colors.info.main}`
}
)}
src={
hasBitbucket
? undefined
: getGHAvatar(pr.author.username)
}
src={getGHAvatar(pr.author.username)}
/>
</Box>
</Box>
Expand Down Expand Up @@ -494,8 +469,6 @@ const PrChangesTooltip: FC<{ pr: PR }> = ({ pr }) => {

const PrReviewersCell: FC<{ pr: PR }> = ({ pr }) => {
const theme = useTheme();
const { user } = useAuth();
const hasBitbucket = user?.integrations?.bitbucket;
return (
<FlexBox
alignCenter
Expand All @@ -509,11 +482,7 @@ const PrReviewersCell: FC<{ pr: PR }> = ({ pr }) => {
<FlexBox
title={
<Box>
<Box>
{hasBitbucket
? reviewer.linked_user?.name || reviewer.username
: `@${reviewer.username}`}
</Box>
<Box>{`@${reviewer.username}`}</Box>
{!reviewer.linked_user && (
<Box fontStyle="italic" color="secondary.dark">
User not added to Middleware
Expand All @@ -522,17 +491,8 @@ const PrReviewersCell: FC<{ pr: PR }> = ({ pr }) => {
</Box>
}
key={reviewer.username}
component={hasBitbucket ? 'div' : Link}
href={
reviewer.linked_user?.id
? `${
ROUTES.COLLABORATE.INSIGHTS.USER.add(reviewer.linked_user.id)
.PATH
}`
: hasBitbucket
? undefined
: getGHAvatar(reviewer.username)
}
component={Link}
href={getGHAvatar(reviewer.username)}
target="_blank"
fontWeight={500}
display="flex"
Expand All @@ -546,7 +506,7 @@ const PrReviewersCell: FC<{ pr: PR }> = ({ pr }) => {
boxShadow: `0 0 0 2px ${theme.colors.info.main}`
}
)}
src={hasBitbucket ? undefined : getGHAvatar(reviewer.username)}
src={getGHAvatar(reviewer.username)}
/>
</FlexBox>
))}
Expand Down
15 changes: 15 additions & 0 deletions web-server/src/constants/overlays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,20 @@ export const overlaysImportMap = {
import('@/content/PullRequests/DeploymentInsightsOverlay').then((c) => ({
default: c.DeploymentInsightsOverlay
}))
),
change_failure_rate: lazy(() =>
import('@/components/OverlayComponents/ChangeFailureRate').then((c) => ({
default: c.ChangeFailureRate
}))
),
all_incidents: lazy(() =>
import('@/content/DoraMetrics/Incidents').then((c) => ({
default: c.AllIncidentsBody
}))
),
resolved_incidents: lazy(() =>
import('@/content/DoraMetrics/ResolvedIncidents').then((c) => ({
default: c.ResolvedIncidentsBody
}))
)
};
122 changes: 122 additions & 0 deletions web-server/src/content/DoraMetrics/DeploymentWithIncidentsMenuItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { AccessTimeRounded, ArrowForwardRounded } from '@mui/icons-material';
import BugReportOutlinedIcon from '@mui/icons-material/BugReportOutlined';
import NearbyErrorIcon from '@mui/icons-material/NearbyError';
import { Card, useTheme } from '@mui/material';
import { format } from 'date-fns';
import Link from 'next/link';
import pluralize from 'pluralize';
import { FC } from 'react';
import { FaExternalLinkAlt } from 'react-icons/fa';

import { FlexBox } from '@/components/FlexBox';
import { Line } from '@/components/Text';
import { DeploymentWithIncidents } from '@/types/resources';
import { getDurationString } from '@/utils/date';
import { OPEN_IN_NEW_TAB_PROPS } from '@/utils/url';

export const DeploymentWithIncidentsMenuItem: FC<{
deployment: DeploymentWithIncidents;
selected: boolean;
onSelect: (dep: DeploymentWithIncidents) => any;
}> = ({ deployment, selected, onSelect }) => {
const theme = useTheme();
const incidents = deployment.incidents;

return (
<FlexBox
key={deployment.id}
component={Card}
p={1}
width="280px"
pointer
sx={{
bgcolor: !onSelect && selected ? theme.colors.info.lighter : undefined,
transition: 'background-color 0.2s',
boxShadow: selected ? `0 0 0 3px ${theme.colors.info.main}` : undefined,
':hover': { bgcolor: theme.colors.info.lighter }
}}
onClick={() => onSelect(deployment)}
>
<FlexBox fill alignCenter justifyBetween gap1>
<FlexBox col flex1>
<FlexBox alignCenter gap={1 / 2}>
<NearbyErrorIcon color="error" fontSize="inherit" />
<Line tiny bold>
Run on{' '}
{format(new Date(deployment.conducted_at), 'do, MMM - hh:mmaaa')}
</Line>
{deployment.html_url && (
<Link
passHref
href={deployment.html_url}
{...OPEN_IN_NEW_TAB_PROPS}
>
<Line
tiny
sx={{
transform: 'scale(0.9)',
transition: 'all 0.2s',
':hover': { color: 'info.main' }
}}
>
<FaExternalLinkAlt />
</Line>
</Link>
)}
</FlexBox>
<FlexBox alignCenter justifyBetween fullWidth>
<FlexBox
alignCenter
gap={1 / 4}
title={`This deployment may have led to ${
incidents.length
} ${pluralize('incident', incidents.length)}`}
tooltipPlacement="left"
>
<BugReportOutlinedIcon fontSize="inherit" />
<Line
tiny
sx={{ whiteSpace: 'nowrap', transform: 'translateY(1px)' }}
>
{incidents.length} {pluralize('incident', incidents.length)}
</Line>
<Line
small
mono
px={1 / 2}
color="info"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
maxWidth: '100px'
}}
>
{deployment.head_branch}
</Line>
</FlexBox>
<FlexBox
alignCenter
gap={1 / 4}
title={`This deployment took ${getDurationString(
deployment.run_duration
)} to run`}
tooltipPlacement="right"
>
<AccessTimeRounded fontSize="inherit" />
<Line small sx={{ whiteSpace: 'nowrap' }}>
{getDurationString(deployment.run_duration)}
</Line>
</FlexBox>
</FlexBox>
</FlexBox>
{onSelect && (
<ArrowForwardRounded
fontSize="small"
color={selected ? 'info' : undefined}
/>
)}
</FlexBox>
</FlexBox>
);
};
Loading