generatePerformanceEventView.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import {Location} from 'history';
  2. import trimStart from 'lodash/trimStart';
  3. import {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
  4. import {wrapQueryInWildcards} from 'sentry/components/performance/searchBar';
  5. import {t} from 'sentry/locale';
  6. import {NewQuery, Organization, Project} from 'sentry/types';
  7. import EventView from 'sentry/utils/discover/eventView';
  8. import {WEB_VITAL_DETAILS} from 'sentry/utils/performance/vitals/constants';
  9. import {decodeScalar} from 'sentry/utils/queryString';
  10. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  11. import {getCurrentTrendParameter} from 'sentry/views/performance/trends/utils';
  12. import {getMiddleTimestamp} from 'sentry/views/starfish/utils/dates';
  13. const DEFAULT_STATS_PERIOD = '7d';
  14. const TOKEN_KEYS_SUPPORTED_IN_LIMITED_SEARCH = ['transaction'];
  15. export const TIME_SPENT_IN_SERVICE =
  16. 'equation|sum(transaction.duration) / total.transaction_duration';
  17. export const getDefaultStatsPeriod = (organization: Organization) => {
  18. if (organization?.features?.includes('performance-landing-page-stats-period')) {
  19. return '14d';
  20. }
  21. return DEFAULT_STATS_PERIOD;
  22. };
  23. function prepareQueryForLandingPage(searchQuery, withStaticFilters) {
  24. const conditions = new MutableSearch(searchQuery);
  25. // If there is a bare text search, we want to treat it as a search
  26. // on the transaction name.
  27. if (conditions.freeText.length > 0) {
  28. const parsedFreeText = conditions.freeText.join(' ');
  29. // the query here is a user entered condition, no need to escape it
  30. conditions.setFilterValues(
  31. 'transaction',
  32. [wrapQueryInWildcards(parsedFreeText)],
  33. false
  34. );
  35. conditions.freeText = [];
  36. }
  37. if (withStaticFilters) {
  38. conditions.tokens = conditions.tokens.filter(
  39. token => token.key && TOKEN_KEYS_SUPPORTED_IN_LIMITED_SEARCH.includes(token.key)
  40. );
  41. }
  42. return conditions.formatString();
  43. }
  44. function generateGenericPerformanceEventView(
  45. location: Location,
  46. withStaticFilters: boolean,
  47. organization: Organization
  48. ): EventView {
  49. const {query} = location;
  50. const fields = ['transaction', 'http.method', 'tpm()', 'p50()', 'p95()', 'project'];
  51. const hasStartAndEnd = query.start && query.end;
  52. const savedQuery: NewQuery = {
  53. id: undefined,
  54. name: t('Performance'),
  55. query: 'event.type:transaction has:http.method',
  56. projects: [],
  57. fields,
  58. version: 2,
  59. };
  60. const widths = Array(savedQuery.fields.length).fill(COL_WIDTH_UNDEFINED);
  61. widths[savedQuery.fields.length - 1] = '110';
  62. savedQuery.widths = widths;
  63. if (!query.statsPeriod && !hasStartAndEnd) {
  64. savedQuery.range = getDefaultStatsPeriod(organization);
  65. }
  66. savedQuery.orderby = decodeScalar(query.sort, '-tpm');
  67. const searchQuery = decodeScalar(query.query, '');
  68. savedQuery.query = prepareQueryForLandingPage(searchQuery, withStaticFilters);
  69. const eventView = EventView.fromNewQueryWithLocation(savedQuery, location);
  70. eventView.additionalConditions.addFilterValues('event.type', ['transaction']);
  71. if (query.trendParameter) {
  72. // projects and projectIds are not necessary here since trendParameter will always
  73. // be present in location and will not be determined based on the project type
  74. const trendParameter = getCurrentTrendParameter(location, [], []);
  75. if (WEB_VITAL_DETAILS[trendParameter.column]) {
  76. eventView.additionalConditions.addFilterValues('has', [trendParameter.column]);
  77. }
  78. }
  79. return eventView;
  80. }
  81. export function generatePerformanceEventView(
  82. location: Location,
  83. _: Project[],
  84. {isTrends = false, withStaticFilters = false} = {},
  85. organization: Organization
  86. ) {
  87. const eventView = generateGenericPerformanceEventView(
  88. location,
  89. withStaticFilters,
  90. organization
  91. );
  92. if (isTrends) {
  93. return eventView;
  94. }
  95. return eventView;
  96. }
  97. export function generateWebServiceEventView(
  98. location: Location,
  99. _: Project[],
  100. {withStaticFilters = false} = {},
  101. organization: Organization
  102. ) {
  103. const {query} = location;
  104. const hasStartAndEnd = query.start && query.end;
  105. const middleTimestamp = getMiddleTimestamp({
  106. start: decodeScalar(query.start),
  107. end: decodeScalar(query.end),
  108. statsPeriod:
  109. !query.statsPeriod && !hasStartAndEnd
  110. ? getDefaultStatsPeriod(organization)
  111. : decodeScalar(query.statsPeriod),
  112. });
  113. const deltaColName = `equation|(percentile_range(transaction.duration,0.50,lessOrEquals,${middleTimestamp})-percentile_range(transaction.duration,0.50,greater,${middleTimestamp}))/percentile_range(transaction.duration,0.50,lessOrEquals,${middleTimestamp})`;
  114. let orderby = decodeScalar(query.sort, `-${TIME_SPENT_IN_SERVICE}`);
  115. const isDescending = orderby.startsWith('-');
  116. const rawOrderby = trimStart(orderby, '-');
  117. if (
  118. rawOrderby.startsWith(
  119. 'equation|(percentile_range(transaction.duration,0.50,lessOrEquals,'
  120. )
  121. ) {
  122. orderby = isDescending ? `-${deltaColName}` : deltaColName;
  123. }
  124. const fields = [
  125. 'transaction',
  126. 'http.method',
  127. 'tpm()',
  128. 'p50()',
  129. deltaColName,
  130. 'count_if(http.status_code,greaterOrEquals,500)',
  131. TIME_SPENT_IN_SERVICE,
  132. 'total.transaction_duration',
  133. 'sum(transaction.duration)',
  134. `percentile_range(transaction.duration,0.50,lessOrEquals,${middleTimestamp})`,
  135. `percentile_range(transaction.duration,0.50,greater,${middleTimestamp})`,
  136. ];
  137. const savedQuery: NewQuery = {
  138. id: undefined,
  139. name: t('Performance'),
  140. query: 'event.type:transaction has:http.method transaction.op:http.server',
  141. projects: [],
  142. fields,
  143. version: 2,
  144. };
  145. const widths = Array(savedQuery.fields.length).fill(COL_WIDTH_UNDEFINED);
  146. widths[savedQuery.fields.length - 1] = '110';
  147. savedQuery.widths = widths;
  148. if (!query.statsPeriod && !hasStartAndEnd) {
  149. savedQuery.range = getDefaultStatsPeriod(organization);
  150. }
  151. savedQuery.orderby = orderby;
  152. const searchQuery = decodeScalar(query.query, '');
  153. savedQuery.query = `${savedQuery.query} ${prepareQueryForLandingPage(
  154. searchQuery,
  155. withStaticFilters
  156. )}`;
  157. const eventView = EventView.fromNewQueryWithLocation(savedQuery, location);
  158. return eventView;
  159. }