123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- import {browserHistory} from 'react-router';
- import type {Location} from 'history';
- import omit from 'lodash/omit';
- import {t} from 'sentry/locale';
- import type {Organization, Project} from 'sentry/types';
- import {trackAnalytics} from 'sentry/utils/analytics';
- import type EventView from 'sentry/utils/discover/eventView';
- import {
- formatAbbreviatedNumber,
- formatFloat,
- formatPercentage,
- getDuration,
- } from 'sentry/utils/formatters';
- import type {HistogramData} from 'sentry/utils/performance/histogram/types';
- import {decodeScalar} from 'sentry/utils/queryString';
- import {MutableSearch} from 'sentry/utils/tokenizeSearch';
- import type {AxisOption} from '../data';
- import {getTermHelp, PerformanceTerm} from '../data';
- import type {Rectangle} from '../transactionSummary/transactionVitals/types';
- import {platformToPerformanceType, ProjectPerformanceType} from '../utils';
- export const LEFT_AXIS_QUERY_KEY = 'left';
- export const RIGHT_AXIS_QUERY_KEY = 'right';
- type LandingDisplay = {
- field: LandingDisplayField;
- label: string;
- };
- export enum LandingDisplayField {
- ALL = 'all',
- FRONTEND_PAGELOAD = 'frontend_pageload',
- FRONTEND_OTHER = 'frontend_other',
- BACKEND = 'backend',
- MOBILE = 'mobile',
- }
- // TODO Abdullah Khan: Remove code for Web Vitals tab in performance landing
- // page when new starfish web vitals module is mature.
- export const LANDING_DISPLAYS = [
- {
- label: t('All Transactions'),
- field: LandingDisplayField.ALL,
- },
- {
- label: t('Frontend'),
- field: LandingDisplayField.FRONTEND_OTHER,
- },
- {
- label: t('Backend'),
- field: LandingDisplayField.BACKEND,
- },
- {
- label: t('Mobile'),
- field: LandingDisplayField.MOBILE,
- },
- ];
- export function excludeTransaction(
- transaction: string | React.ReactText,
- props: {eventView: EventView; location: Location}
- ) {
- const {eventView, location} = props;
- const searchConditions = new MutableSearch(eventView.query);
- searchConditions.addFilterValues('!transaction', [`${transaction}`]);
- browserHistory.push({
- pathname: location.pathname,
- query: {
- ...location.query,
- cursor: undefined,
- query: searchConditions.formatString(),
- },
- });
- }
- export function getLandingDisplayFromParam(location: Location) {
- const landingField = decodeScalar(location?.query?.landingDisplay);
- const display = LANDING_DISPLAYS.find(({field}) => field === landingField);
- return display;
- }
- export function getDefaultDisplayForPlatform(projects: Project[], eventView?: EventView) {
- const defaultDisplayField = getDefaultDisplayFieldForPlatform(projects, eventView);
- const defaultDisplay = LANDING_DISPLAYS.find(
- ({field}) => field === defaultDisplayField
- );
- return defaultDisplay || LANDING_DISPLAYS[0];
- }
- export function getCurrentLandingDisplay(
- location: Location,
- projects: Project[],
- eventView?: EventView
- ): LandingDisplay {
- const display = getLandingDisplayFromParam(location);
- if (display) {
- return display;
- }
- return getDefaultDisplayForPlatform(projects, eventView);
- }
- export function handleLandingDisplayChange(
- field: LandingDisplayField,
- location: Location,
- projects: Project[],
- organization: Organization,
- eventView?: EventView
- ) {
- // Transaction op can affect the display and show no results if it is explicitly set.
- const query = decodeScalar(location.query.query, '');
- const searchConditions = new MutableSearch(query);
- searchConditions.removeFilter('transaction.op');
- const queryWithConditions = {
- ...omit(location.query, ['landingDisplay', 'sort']),
- query: searchConditions.formatString(),
- };
- delete queryWithConditions[LEFT_AXIS_QUERY_KEY];
- delete queryWithConditions[RIGHT_AXIS_QUERY_KEY];
- const defaultDisplay = getDefaultDisplayFieldForPlatform(projects, eventView);
- const currentDisplay = getCurrentLandingDisplay(location, projects, eventView).field;
- const newQuery: {query: string; landingDisplay?: LandingDisplayField} =
- defaultDisplay === field
- ? {...queryWithConditions}
- : {...queryWithConditions, landingDisplay: field};
- trackAnalytics('performance_views.landingv3.display_change', {
- organization,
- change_to_display: field,
- default_display: defaultDisplay,
- current_display: currentDisplay,
- is_default: defaultDisplay === currentDisplay,
- });
- browserHistory.push({
- pathname: location.pathname,
- query: newQuery,
- });
- }
- export function getChartWidth(chartData: HistogramData, refPixelRect: Rectangle | null) {
- const distance = refPixelRect ? refPixelRect.point2.x - refPixelRect.point1.x : 0;
- const chartWidth = chartData.length * distance;
- return {
- chartWidth,
- };
- }
- export function getDefaultDisplayFieldForPlatform(
- projects: Project[],
- eventView?: EventView
- ) {
- if (!eventView) {
- return LandingDisplayField.ALL;
- }
- const projectIds = eventView.project;
- const performanceTypeToDisplay = {
- [ProjectPerformanceType.ANY]: LandingDisplayField.ALL,
- [ProjectPerformanceType.FRONTEND]: LandingDisplayField.FRONTEND_OTHER,
- [ProjectPerformanceType.BACKEND]: LandingDisplayField.BACKEND,
- [ProjectPerformanceType.MOBILE]: LandingDisplayField.MOBILE,
- };
- const performanceType = platformToPerformanceType(projects, projectIds);
- const landingField =
- performanceTypeToDisplay[performanceType] ?? LandingDisplayField.ALL;
- return landingField;
- }
- type VitalCardDetail = {
- formatter: (value: number) => string | number;
- title: string;
- tooltip: string;
- };
- export const vitalCardDetails = (
- organization: Organization
- ): {[key: string]: VitalCardDetail | undefined} => {
- return {
- 'p75(transaction.duration)': {
- title: t('Duration (p75)'),
- tooltip: getTermHelp(organization, PerformanceTerm.P75),
- formatter: value => getDuration(value / 1000, value >= 1000 ? 3 : 0, true),
- },
- 'tpm()': {
- title: t('Throughput'),
- tooltip: getTermHelp(organization, PerformanceTerm.THROUGHPUT),
- formatter: (value: number | string) => formatAbbreviatedNumber(value),
- },
- 'failure_rate()': {
- title: t('Failure Rate'),
- tooltip: getTermHelp(organization, PerformanceTerm.FAILURE_RATE),
- formatter: value => formatPercentage(value, 2),
- },
- 'apdex()': {
- title: t('Apdex'),
- tooltip: getTermHelp(organization, PerformanceTerm.APDEX),
- formatter: value => formatFloat(value, 4),
- },
- 'p75(measurements.frames_slow_rate)': {
- title: t('Slow Frames (p75)'),
- tooltip: getTermHelp(organization, PerformanceTerm.SLOW_FRAMES),
- formatter: value => formatPercentage(value, 2),
- },
- 'p75(measurements.frames_frozen_rate)': {
- title: t('Frozen Frames (p75)'),
- tooltip: getTermHelp(organization, PerformanceTerm.FROZEN_FRAMES),
- formatter: value => formatPercentage(value, 2),
- },
- 'p75(measurements.app_start_cold)': {
- title: t('Cold Start (p75)'),
- tooltip: getTermHelp(organization, PerformanceTerm.APP_START_COLD),
- formatter: value => getDuration(value / 1000, value >= 1000 ? 3 : 0, true),
- },
- 'p75(measurements.app_start_warm)': {
- title: t('Warm Start (p75)'),
- tooltip: getTermHelp(organization, PerformanceTerm.APP_START_WARM),
- formatter: value => getDuration(value / 1000, value >= 1000 ? 3 : 0, true),
- },
- 'p75(measurements.stall_percentage)': {
- title: t('Stall Percentage (p75)'),
- tooltip: getTermHelp(organization, PerformanceTerm.STALL_PERCENTAGE),
- formatter: value => formatPercentage(value, 2),
- },
- };
- };
- export function getDisplayAxes(options: AxisOption[], location: Location) {
- const leftDefault = options.find(opt => opt.isLeftDefault) || options[0];
- const rightDefault = options.find(opt => opt.isRightDefault) || options[1];
- const leftAxis =
- options.find(opt => opt.value === location.query[LEFT_AXIS_QUERY_KEY]) || leftDefault;
- const rightAxis =
- options.find(opt => opt.value === location.query[RIGHT_AXIS_QUERY_KEY]) ||
- rightDefault;
- return {
- leftAxis,
- rightAxis,
- };
- }
- export function checkIsReactNative(eventView) {
- // only react native should contain the stall percentage column
- return Boolean(
- eventView.getFields().find(field => field.includes('measurements.stall_percentage'))
- );
- }
|