replaySearchBar.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import {useCallback} from 'react';
  2. import {fetchTagValues} from 'sentry/actionCreators/tags';
  3. import SmartSearchBar from 'sentry/components/smartSearchBar';
  4. import {MAX_QUERY_LENGTH, NEGATION_OPERATOR, SEARCH_WILDCARD} from 'sentry/constants';
  5. import {t} from 'sentry/locale';
  6. import {
  7. Organization,
  8. PageFilters,
  9. SavedSearchType,
  10. Tag,
  11. TagCollection,
  12. TagValue,
  13. } from 'sentry/types';
  14. import {isAggregateField} from 'sentry/utils/discover/fields';
  15. import {getFieldDefinition, REPLAY_FIELDS} from 'sentry/utils/fields';
  16. import useApi from 'sentry/utils/useApi';
  17. const SEARCH_SPECIAL_CHARS_REGEXP = new RegExp(
  18. `^${NEGATION_OPERATOR}|\\${SEARCH_WILDCARD}`,
  19. 'g'
  20. );
  21. /**
  22. * Prepare query string (e.g. strip special characters like negation operator)
  23. */
  24. function prepareQuery(searchQuery: string) {
  25. return searchQuery.replace(SEARCH_SPECIAL_CHARS_REGEXP, '');
  26. }
  27. const getReplayFieldDefinition = (key: string) => getFieldDefinition(key, 'replay');
  28. function fieldDefinitionsToTagCollection(fieldKeys: string[]): TagCollection {
  29. return Object.fromEntries(
  30. fieldKeys.map(key => [
  31. key,
  32. {
  33. key,
  34. name: key,
  35. kind: getReplayFieldDefinition(key)?.kind,
  36. },
  37. ])
  38. );
  39. }
  40. const REPLAY_TAGS = fieldDefinitionsToTagCollection(REPLAY_FIELDS);
  41. type Props = React.ComponentProps<typeof SmartSearchBar> & {
  42. organization: Organization;
  43. pageFilters: PageFilters;
  44. };
  45. function ReplaySearchBar(props: Props) {
  46. const {organization, pageFilters} = props;
  47. const api = useApi();
  48. const projectIdStrings = pageFilters.projects?.map(String);
  49. const getTagValues = useCallback(
  50. (tag: Tag, searchQuery: string, _params: object): Promise<string[]> => {
  51. if (isAggregateField(tag.key)) {
  52. // We can't really auto suggest values for aggregate fields
  53. // or measurements, so we simply don't
  54. return Promise.resolve([]);
  55. }
  56. return fetchTagValues({
  57. api,
  58. orgSlug: organization.slug,
  59. tagKey: tag.key,
  60. search: searchQuery,
  61. projectIds: projectIdStrings,
  62. includeReplays: true,
  63. }).then(
  64. tagValues => (tagValues as TagValue[]).map(({value}) => value),
  65. () => {
  66. throw new Error('Unable to fetch event field values');
  67. }
  68. );
  69. },
  70. [api, organization.slug, projectIdStrings]
  71. );
  72. return (
  73. <SmartSearchBar
  74. {...props}
  75. onGetTagValues={getTagValues}
  76. supportedTags={REPLAY_TAGS}
  77. placeholder={t('Search for users, duration, count_errors, and more')}
  78. prepareQuery={prepareQuery}
  79. maxQueryLength={MAX_QUERY_LENGTH}
  80. searchSource="replay_index"
  81. savedSearchType={SavedSearchType.REPLAY}
  82. maxMenuHeight={500}
  83. hasRecentSearches
  84. fieldDefinitionGetter={getReplayFieldDefinition}
  85. />
  86. );
  87. }
  88. export default ReplaySearchBar;