resourceLandingPageCharts.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import styled from '@emotion/styled';
  2. import {space} from 'sentry/styles/space';
  3. import {EMPTY_OPTION_VALUE, MutableSearch} from 'sentry/utils/tokenizeSearch';
  4. import {useSynchronizeCharts} from 'sentry/views/insights/common/components/chart';
  5. import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
  6. import type {ModuleFilters} from 'sentry/views/insights/common/views/spans/useModuleFilters';
  7. import {SpanMetricsField} from 'sentry/views/insights/types';
  8. import {DurationChart} from './durationChart';
  9. import {ThroughputChart} from './throughputChart';
  10. const {SPAN_SELF_TIME, SPAN_DESCRIPTION, SPAN_DOMAIN} = SpanMetricsField;
  11. type Props = {
  12. appliedFilters: ModuleFilters;
  13. extraQuery?: string[];
  14. };
  15. export function ResourceLandingPageCharts({appliedFilters, extraQuery}: Props) {
  16. let query: string = buildDiscoverQueryConditions(appliedFilters);
  17. if (extraQuery) {
  18. query += ` ${extraQuery.join(' ')}`;
  19. }
  20. const {data, isPending, error} = useSpanMetricsSeries(
  21. {
  22. search: new MutableSearch(query),
  23. yAxis: ['spm()', `avg(${SPAN_SELF_TIME})`],
  24. },
  25. 'api.starfish.span-time-charts'
  26. );
  27. useSynchronizeCharts(1, !isPending);
  28. return (
  29. <ChartsContainer>
  30. <ChartsContainerItem>
  31. <ThroughputChart series={data['spm()']} isLoading={isPending} error={error} />
  32. </ChartsContainerItem>
  33. <ChartsContainerItem>
  34. <DurationChart
  35. series={[data[`avg(${SPAN_SELF_TIME})`]]}
  36. isLoading={isPending}
  37. error={error}
  38. />
  39. </ChartsContainerItem>
  40. </ChartsContainer>
  41. );
  42. }
  43. const SPAN_FILTER_KEYS = ['span_operation', SPAN_DOMAIN, 'action'];
  44. const buildDiscoverQueryConditions = (appliedFilters: ModuleFilters) => {
  45. const result = Object.keys(appliedFilters)
  46. .filter(key => SPAN_FILTER_KEYS.includes(key))
  47. .filter(key => Boolean(appliedFilters[key]))
  48. .map(key => {
  49. const value = appliedFilters[key];
  50. if (key === SPAN_DOMAIN && value === EMPTY_OPTION_VALUE) {
  51. return [`!has:${SPAN_DOMAIN}`];
  52. }
  53. return `${key}:${value}`;
  54. });
  55. result.push(`has:${SPAN_DESCRIPTION}`);
  56. return result.join(' ');
  57. };
  58. const ChartsContainer = styled('div')`
  59. display: flex;
  60. flex-direction: row;
  61. flex-wrap: wrap;
  62. gap: ${space(2)};
  63. `;
  64. const ChartsContainerItem = styled('div')`
  65. flex: 1;
  66. `;