thresholdStatuses.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import {useCallback, useEffect, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import type {Client} from 'sentry/api';
  4. import * as SidebarSection from 'sentry/components/sidebarSection';
  5. import {IconCheckmark, IconWarning} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import type {Organization, Release, ReleaseProject} from 'sentry/types';
  9. import {getExactDuration} from 'sentry/utils/formatters';
  10. import useApi from 'sentry/utils/useApi';
  11. import {
  12. ReleaseDetailsTable,
  13. ReleaseDetailsTableRow,
  14. } from '../../../components/releaseDetailsSideTable';
  15. import {fetchThresholdStatuses} from '../../../utils/fetchThresholdStatus';
  16. import type {ThresholdStatus, ThresholdStatusesQuery} from '../../../utils/types';
  17. type Props = {
  18. organization: Organization;
  19. project: Required<ReleaseProject>;
  20. release: Release;
  21. selectedEnvs: string[];
  22. };
  23. function ThresholdStatuses({project, release, organization, selectedEnvs}: Props) {
  24. const api: Client = useApi();
  25. const [thresholdStatuses, setThresholdStatuses] = useState<ThresholdStatus[]>([]);
  26. const fetchThresholdStatusCallback = useCallback(async () => {
  27. const fuzzSec = 30;
  28. const start = new Date(new Date(release.dateCreated).getTime() - fuzzSec * 1000);
  29. const end = new Date(new Date(release.dateCreated).getTime() + fuzzSec * 1000);
  30. const releaseVersion: string = release.version;
  31. const query: ThresholdStatusesQuery = {
  32. start: start.toISOString(),
  33. end: end.toISOString(),
  34. release: [releaseVersion],
  35. projectSlug: [project.slug],
  36. };
  37. if (selectedEnvs.length) {
  38. query.environment = selectedEnvs;
  39. }
  40. return await fetchThresholdStatuses(organization, api, query);
  41. }, [release, project, selectedEnvs, organization, api]);
  42. useEffect(() => {
  43. fetchThresholdStatusCallback().then(thresholds => {
  44. const list = thresholds[`${project.slug}-${release.version}`];
  45. const sorted =
  46. list?.sort((a, b) => {
  47. const keyA: string = a.environment ? a.environment.name : '';
  48. const keyB: string = b.environment ? b.environment.name : '';
  49. return keyA.localeCompare(keyB);
  50. }) || [];
  51. setThresholdStatuses(sorted);
  52. });
  53. }, [fetchThresholdStatusCallback, project, release]);
  54. if (thresholdStatuses.length > 0) {
  55. return (
  56. <SidebarSection.Wrap>
  57. <SidebarSection.Title>{t('Threshold Statuses')}</SidebarSection.Title>
  58. <SidebarSection.Content>
  59. <ReleaseDetailsTable>
  60. {thresholdStatuses?.map(status => (
  61. <ReleaseDetailsTableRow
  62. key={status.id}
  63. type={status.is_healthy ? undefined : 'error'}
  64. >
  65. <RowGrid>
  66. <div>{status.environment?.name}</div>
  67. <div>{getExactDuration(status.window_in_seconds, true, 'seconds')}</div>
  68. <div>
  69. {status.threshold_type} {status.trigger_type === 'over' ? '>' : '<'}{' '}
  70. {status.value}
  71. </div>
  72. <AlignRight>
  73. {status.is_healthy ? (
  74. <IconCheckmark color="successText" size="xs" />
  75. ) : (
  76. <IconWarning color="errorText" size="xs" />
  77. )}
  78. </AlignRight>
  79. </RowGrid>
  80. </ReleaseDetailsTableRow>
  81. ))}
  82. </ReleaseDetailsTable>
  83. </SidebarSection.Content>
  84. </SidebarSection.Wrap>
  85. );
  86. }
  87. return null;
  88. }
  89. const AlignRight = styled('div')`
  90. text-align: right;
  91. `;
  92. const RowGrid = styled('div')`
  93. display: grid;
  94. grid-template-columns: 0.5fr 0.5fr max-content 0.1fr;
  95. gap: ${space(1)};
  96. `;
  97. export default ThresholdStatuses;