metricField.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import {Fragment} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {Button} from 'sentry/components/button';
  5. import FormField, {FormFieldProps} from 'sentry/components/forms/formField';
  6. import {Organization} from 'sentry/types';
  7. import {
  8. Aggregation,
  9. AGGREGATIONS,
  10. explodeFieldString,
  11. generateFieldAsString,
  12. } from 'sentry/utils/discover/fields';
  13. import {
  14. AlertType,
  15. hideParameterSelectorSet,
  16. hidePrimarySelectorSet,
  17. } from 'sentry/views/alerts/wizard/options';
  18. import {QueryField} from 'sentry/views/discover/table/queryField';
  19. import {FieldValueKind} from 'sentry/views/discover/table/types';
  20. import {generateFieldOptions} from 'sentry/views/discover/utils';
  21. import {
  22. errorFieldConfig,
  23. getWizardAlertFieldConfig,
  24. OptionConfig,
  25. transactionFieldConfig,
  26. } from './constants';
  27. import {Dataset} from './types';
  28. type Props = Omit<FormFieldProps, 'children'> & {
  29. organization: Organization;
  30. alertType?: AlertType;
  31. /**
  32. * Optionally set a width for each column of selector
  33. */
  34. columnWidth?: number;
  35. inFieldLabels?: boolean;
  36. };
  37. export const getFieldOptionConfig = ({
  38. dataset,
  39. alertType,
  40. }: {
  41. dataset: Dataset;
  42. alertType?: AlertType;
  43. }) => {
  44. let config: OptionConfig;
  45. let hidePrimarySelector = false;
  46. let hideParameterSelector = false;
  47. if (alertType) {
  48. config = getWizardAlertFieldConfig(alertType, dataset);
  49. hidePrimarySelector = hidePrimarySelectorSet.has(alertType);
  50. hideParameterSelector = hideParameterSelectorSet.has(alertType);
  51. } else {
  52. config = dataset === Dataset.ERRORS ? errorFieldConfig : transactionFieldConfig;
  53. }
  54. const aggregations = Object.fromEntries<Aggregation>(
  55. config.aggregations.map(key => {
  56. // TODO(scttcper): Temporary hack for default value while we handle the translation of user
  57. if (key === 'count_unique') {
  58. const agg = AGGREGATIONS[key] as Aggregation;
  59. agg.getFieldOverrides = () => {
  60. return {defaultValue: 'tags[sentry:user]'};
  61. };
  62. return [key, agg];
  63. }
  64. return [key, AGGREGATIONS[key]];
  65. })
  66. );
  67. const fieldKeys = config.fields.map(key => {
  68. // XXX(epurkhiser): Temporary hack while we handle the translation of user ->
  69. // tags[sentry:user].
  70. if (key === 'user') {
  71. return 'tags[sentry:user]';
  72. }
  73. return key;
  74. });
  75. const {measurementKeys, spanOperationBreakdownKeys} = config;
  76. return {
  77. fieldOptionsConfig: {
  78. aggregations,
  79. fieldKeys,
  80. measurementKeys,
  81. spanOperationBreakdownKeys,
  82. },
  83. hidePrimarySelector,
  84. hideParameterSelector,
  85. };
  86. };
  87. function MetricField({
  88. organization,
  89. columnWidth,
  90. inFieldLabels,
  91. alertType,
  92. ...props
  93. }: Props) {
  94. return (
  95. <FormField {...props}>
  96. {({onChange, value, model, disabled}) => {
  97. const dataset = model.getValue('dataset');
  98. const {fieldOptionsConfig, hidePrimarySelector, hideParameterSelector} =
  99. getFieldOptionConfig({
  100. dataset: dataset as Dataset,
  101. alertType,
  102. });
  103. const fieldOptions = generateFieldOptions({organization, ...fieldOptionsConfig});
  104. const fieldValue = explodeFieldString(value ?? '');
  105. const fieldKey =
  106. fieldValue?.kind === FieldValueKind.FUNCTION
  107. ? `function:${fieldValue.function[0]}`
  108. : '';
  109. const selectedField = fieldOptions[fieldKey]?.value;
  110. const numParameters: number =
  111. selectedField?.kind === FieldValueKind.FUNCTION
  112. ? selectedField.meta.parameters.length
  113. : 0;
  114. const parameterColumns =
  115. numParameters - (hideParameterSelector ? 1 : 0) - (hidePrimarySelector ? 1 : 0);
  116. return (
  117. <Fragment>
  118. <StyledQueryField
  119. filterPrimaryOptions={option =>
  120. option.value.kind === FieldValueKind.FUNCTION
  121. }
  122. fieldOptions={fieldOptions}
  123. fieldValue={fieldValue}
  124. onChange={v => onChange(generateFieldAsString(v), {})}
  125. columnWidth={columnWidth}
  126. gridColumns={parameterColumns + 1}
  127. inFieldLabels={inFieldLabels}
  128. shouldRenderTag={false}
  129. disabled={disabled}
  130. hideParameterSelector={hideParameterSelector}
  131. hidePrimarySelector={hidePrimarySelector}
  132. />
  133. </Fragment>
  134. );
  135. }}
  136. </FormField>
  137. );
  138. }
  139. const StyledQueryField = styled(QueryField)<{gridColumns: number; columnWidth?: number}>`
  140. ${p =>
  141. p.columnWidth &&
  142. css`
  143. width: ${p.gridColumns * p.columnWidth}px;
  144. `}
  145. `;
  146. const PresetButton = styled(Button)<{disabled: boolean}>`
  147. ${p =>
  148. p.disabled &&
  149. css`
  150. color: ${p.theme.textColor};
  151. &:hover,
  152. &:focus {
  153. color: ${p.theme.textColor};
  154. }
  155. `}
  156. `;
  157. PresetButton.defaultProps = {
  158. priority: 'link',
  159. borderless: true,
  160. };
  161. export default MetricField;