Browse Source

feat(browser-starish): resource module ui changes (#58768)

Bunch of changes to work towards addressing UI differences in
https://github.com/getsentry/sentry/issues/58723
Didn't want to do it all in one PR to make the review more managable

1. add selector for JS/CSS and Images
<img width="928" alt="image"
src="https://github.com/getsentry/sentry/assets/44422760/3429a2f5-4958-426b-a56b-9200f6ca2d58">
- This still needs to be hooked up to a image view.

- This PR makes the change so that the JS/css filters on
`resource.script` and `resource.css`


2. Add resource size to the resource summary page
<img width="919" alt="image"
src="https://github.com/getsentry/sentry/assets/44422760/a2c0e87d-6f32-4d0a-8e7b-b36ece0f7930">
- For some reason these are all 0's tho, need to look into that.

3. Remove the `uncompressed column` as this was not possible to obtain
using a timeseries.

4. Add a render blocking resource toggle switch
Dominik Buszowiecki 1 year ago
parent
commit
b4d92b1085

+ 5 - 0
static/app/views/performance/browser/resources/imageResourceView.tsx

@@ -0,0 +1,5 @@
+function ImageResourceView() {
+  return <div>Add a sample table here, with some filters</div>;
+}
+
+export default ImageResourceView;

+ 50 - 5
static/app/views/performance/browser/resources/index.tsx

@@ -1,3 +1,4 @@
+import {MouseEventHandler} from 'react';
 import {browserHistory} from 'react-router';
 import styled from '@emotion/styled';
 
@@ -10,6 +11,8 @@ import * as Layout from 'sentry/components/layouts/thirds';
 import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
 import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
 import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
+import SwitchButton from 'sentry/components/switchButton';
+import {TabList, Tabs} from 'sentry/components/tabs';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {useLocation} from 'sentry/utils/useLocation';
@@ -26,7 +29,13 @@ import {useResourcePagesQuery} from 'sentry/views/performance/browser/resources/
 import {useResourceSort} from 'sentry/views/performance/browser/resources/utils/useResourceSort';
 import {ModulePageProviders} from 'sentry/views/performance/database/modulePageProviders';
 
-const {RESOURCE_TYPE, SPAN_DOMAIN, TRANSACTION, DESCRIPTION} = BrowserStarfishFields;
+const {
+  RESOURCE_TYPE,
+  SPAN_DOMAIN,
+  TRANSACTION,
+  DESCRIPTION,
+  RESOURCE_RENDER_BLOCKING_STATUS,
+} = BrowserStarfishFields;
 
 type Option = {
   label: string;
@@ -37,6 +46,19 @@ function ResourcesLandingPage() {
   const organization = useOrganization();
   const filters = useResourceModuleFilters();
   const sort = useResourceSort();
+  const location = useLocation();
+
+  const handleBlockingToggle: MouseEventHandler = () => {
+    const hasBlocking = filters[RESOURCE_RENDER_BLOCKING_STATUS] === 'blocking';
+    const newBlocking = hasBlocking ? undefined : 'blocking';
+    browserHistory.push({
+      ...location,
+      query: {
+        ...location.query,
+        [RESOURCE_RENDER_BLOCKING_STATUS]: newBlocking,
+      },
+    });
+  };
 
   return (
     <ModulePageProviders title={[t('Performance'), t('Resources')].join(' — ')}>
@@ -60,6 +82,14 @@ function ResourcesLandingPage() {
             <FeatureBadge type="alpha" />
           </Layout.Title>
         </Layout.HeaderContent>
+        <StyledTabs>
+          <TabList hideBorder>
+            <TabList.Item key="resource.css/script">
+              {t('Javascript/Stylesheets')}
+            </TabList.Item>
+            <TabList.Item key="resource.img">{t('Images')}</TabList.Item>
+          </TabList>
+        </StyledTabs>
       </Layout.Header>
 
       <Layout.Body>
@@ -75,8 +105,14 @@ function ResourcesLandingPage() {
             <DomainSelector value={filters[SPAN_DOMAIN] || ''} />
             <ResourceTypeSelector value={filters[RESOURCE_TYPE] || ''} />
             <PageSelector value={filters[TRANSACTION] || ''} />
+            <SwitchContainer>
+              <SwitchButton
+                isActive={filters[RESOURCE_RENDER_BLOCKING_STATUS] === 'blocking'}
+                toggle={handleBlockingToggle}
+              />
+              {t('Render Blocking')}
+            </SwitchContainer>
           </FilterOptionsContainer>
-
           <ResourceTable sort={sort} />
           <ResourceSidebar groupId={filters[DESCRIPTION]} />
         </Layout.Main>
@@ -121,8 +157,7 @@ function ResourceTypeSelector({value}: {value?: string}) {
   const options: Option[] = [
     {value: '', label: 'All'},
     {value: 'resource.script', label: `${t('JavaScript')} (.js)`},
-    {value: '.css', label: `${t('Stylesheet')} (.css)`},
-    {value: 'resource.img', label: `${t('Images')} (.png, .jpg, .jpeg, .gif, etc)`},
+    {value: 'resource.css', label: `${t('Stylesheet')} (.css)`},
   ];
   return (
     <SelectControlWithProps
@@ -169,6 +204,12 @@ function PageSelector({value}: {value?: string}) {
   );
 }
 
+const SwitchContainer = styled('div')`
+  display: flex;
+  align-items: center;
+  column-gap: ${space(1)};
+`;
+
 function SelectControlWithProps(props: ControlProps & {options: Option[]}) {
   return <SelectControl {...props} />;
 }
@@ -177,9 +218,13 @@ export const PaddedContainer = styled('div')`
   margin-bottom: ${space(2)};
 `;
 
+const StyledTabs = styled(Tabs)`
+  grid-column: 1/-1;
+`;
+
 const FilterOptionsContainer = styled('div')`
   display: grid;
-  grid-template-columns: repeat(3, 1fr);
+  grid-template-columns: repeat(4, 1fr);
   gap: ${space(2)};
   margin-bottom: ${space(2)};
   max-width: 800px;

+ 5 - 0
static/app/views/performance/browser/resources/jsCssResourceView.tsx

@@ -0,0 +1,5 @@
+function ImageResourceView() {
+  return <div>Move things over from index.tsx</div>;
+}
+
+export default ImageResourceView;

+ 6 - 0
static/app/views/performance/browser/resources/resourceSummaryPage/resourceSummaryTable.tsx

@@ -16,6 +16,7 @@ import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/render
 import {ThroughputCell} from 'sentry/views/starfish/components/tableCells/throughputCell';
 
 type Row = {
+  'avg(http.response_content_length)': number;
   'avg(span.self_time)': number;
   'spm()': number;
   transaction: string;
@@ -41,6 +42,11 @@ function ResourceSummaryTable() {
       width: COL_WIDTH_UNDEFINED,
       name: 'Avg Duration',
     },
+    {
+      key: 'avg(http.response_content_length)',
+      width: COL_WIDTH_UNDEFINED,
+      name: 'Avg Resource Size',
+    },
   ];
 
   const renderBodyCell = (col: Column, row: Row) => {

+ 6 - 18
static/app/views/performance/browser/resources/resourceTable.tsx

@@ -18,13 +18,8 @@ import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/render
 import {ThroughputCell} from 'sentry/views/starfish/components/tableCells/throughputCell';
 import {SpanFunction, SpanMetricsField} from 'sentry/views/starfish/types';
 
-const {
-  SPAN_DESCRIPTION,
-  RESOURCE_RENDER_BLOCKING_STATUS,
-  SPAN_OP,
-  SPAN_SELF_TIME,
-  HTTP_RESPONSE_CONTENT_LENGTH,
-} = SpanMetricsField;
+const {SPAN_DESCRIPTION, SPAN_OP, SPAN_SELF_TIME, HTTP_RESPONSE_CONTENT_LENGTH} =
+  SpanMetricsField;
 
 const {SPM} = SpanFunction;
 
@@ -48,7 +43,10 @@ type Props = {
 
 function ResourceTable({sort}: Props) {
   const location = useLocation();
-  const {data, isLoading, pageLinks} = useResourcesQuery({sort});
+  const {data, isLoading, pageLinks} = useResourcesQuery({
+    sort,
+    defaultResourceTypes: ['resource.script', 'resource.css'],
+  });
 
   const columnOrder: GridColumnOrder<keyof Row>[] = [
     {key: SPAN_DESCRIPTION, width: COL_WIDTH_UNDEFINED, name: 'Resource name'},
@@ -64,16 +62,6 @@ function ResourceTable({sort}: Props) {
       width: COL_WIDTH_UNDEFINED,
       name: t('Avg Resource size'),
     },
-    {
-      key: RESOURCE_RENDER_BLOCKING_STATUS,
-      width: COL_WIDTH_UNDEFINED,
-      name: t('Render blocking'),
-    },
-    {
-      key: 'http.decoded_response_content_length',
-      width: COL_WIDTH_UNDEFINED,
-      name: t('Uncompressed'),
-    },
   ];
   const tableData: Row[] = data.length
     ? data.map(span => ({

+ 2 - 1
static/app/views/performance/browser/resources/utils/useResourceFilters.ts

@@ -15,7 +15,8 @@ export type ModuleFilters = {
   [BrowserStarfishFields.RESOURCE_RENDER_BLOCKING_STATUS]:
     | ''
     | 'non-blocking'
-    | 'blocking';
+    | 'blocking'
+    | '!blocking';
   [BrowserStarfishFields.SPAN_DOMAIN]?: string;
   [BrowserStarfishFields.RESOURCE_TYPE]?: 'resource.script' | 'resource.img';
   [BrowserStarfishFields.TRANSACTION]?: string;

+ 6 - 1
static/app/views/performance/browser/resources/utils/useResourcePageQuery.ts

@@ -1,6 +1,11 @@
 import {Sort} from 'sentry/utils/discover/fields';
 import {useSpanTransactionMetrics} from 'sentry/views/starfish/queries/useSpanTransactionMetrics';
+import {SpanMetricsField} from 'sentry/views/starfish/types';
+
+const {HTTP_RESPONSE_CONTENT_LENGTH} = SpanMetricsField;
 
 export const useResourcePagesQuery = (groupId: string, {sort}: {sort: Sort}) => {
-  return useSpanTransactionMetrics({'span.group': groupId}, [sort]);
+  return useSpanTransactionMetrics({'span.group': groupId}, [sort], undefined, [
+    `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`,
+  ]);
 };

+ 11 - 4
static/app/views/performance/browser/resources/utils/useResourcesQuery.ts

@@ -18,13 +18,21 @@ const {
   HTTP_RESPONSE_CONTENT_LENGTH,
 } = SpanMetricsField;
 
-export const useResourcesQuery = ({sort}: {sort: ValidSort}) => {
+type Props = {
+  sort: ValidSort;
+  defaultResourceTypes?: string[];
+};
+
+export const useResourcesQuery = ({sort, defaultResourceTypes}: Props) => {
   const pageFilters = usePageFilters();
   const location = useLocation();
   const resourceFilters = useResourceModuleFilters();
   const {slug: orgSlug} = useOrganization();
+
   const queryConditions = [
-    `${SPAN_OP}:${resourceFilters.type || 'resource.*'}`,
+    `${SPAN_OP}:${
+      resourceFilters.type || `[${defaultResourceTypes?.join(',')}]` || 'resource.*'
+    }`,
     ...(resourceFilters.transaction
       ? [`transaction:"${resourceFilters.transaction}"`]
       : []),
@@ -35,7 +43,7 @@ export const useResourcesQuery = ({sort}: {sort: ValidSort}) => {
       ? [
           `resource.render_blocking_status:${resourceFilters['resource.render_blocking_status']}`,
         ]
-      : []),
+      : [`!resource.render_blocking_status:blocking`]),
   ];
 
   // TODO - we should be using metrics data here
@@ -48,7 +56,6 @@ export const useResourcesQuery = ({sort}: {sort: ValidSort}) => {
         `avg(${SPAN_SELF_TIME})`,
         'spm()',
         SPAN_GROUP,
-        RESOURCE_RENDER_BLOCKING_STATUS,
         SPAN_DOMAIN,
         `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`,
       ],

+ 10 - 2
static/app/views/starfish/queries/useSpanTransactionMetrics.tsx

@@ -12,6 +12,7 @@ import {useWrappedDiscoverQuery} from 'sentry/views/starfish/utils/useSpansQuery
 const {SPAN_SELF_TIME} = SpanMetricsField;
 
 export type SpanTransactionMetrics = {
+  'avg(http.response_content_length)': number;
   'avg(span.self_time)': number;
   'http_error_count()': number;
   'spm()': number;
@@ -26,12 +27,13 @@ export const useSpanTransactionMetrics = (
   filters: MetricsFilters,
   sorts?: Sort[],
   cursor?: string,
+  extraFields?: string[],
   enabled: boolean = true,
   referrer = 'api.starfish.span-transaction-metrics'
 ) => {
   const location = useLocation();
 
-  const eventView = getEventView(location, filters, sorts);
+  const eventView = getEventView(location, filters, sorts, extraFields);
 
   return useWrappedDiscoverQuery<SpanTransactionMetrics[]>({
     eventView,
@@ -43,7 +45,12 @@ export const useSpanTransactionMetrics = (
   });
 };
 
-function getEventView(location: Location, filters: MetricsFilters = {}, sorts?: Sort[]) {
+function getEventView(
+  location: Location,
+  filters: MetricsFilters = {},
+  sorts?: Sort[],
+  extraFields = [] as string[]
+) {
   const search = new MutableSearch('');
 
   Object.entries(filters).forEach(([key, value]) => {
@@ -71,6 +78,7 @@ function getEventView(location: Location, filters: MetricsFilters = {}, sorts?:
         'time_spent_percentage()',
         'transaction.op',
         'http_error_count()',
+        ...extraFields,
       ],
       orderby: '-time_spent_percentage',
       dataset: DiscoverDatasets.SPANS_METRICS,

+ 1 - 0
static/app/views/starfish/views/spanSummaryPage/spanTransactionsTable.tsx

@@ -78,6 +78,7 @@ export function SpanTransactionsTable({span, endpoint, endpointMethod, sort}: Pr
     },
     [sort],
     cursor,
+    [],
     Boolean(span[SpanMetricsField.SPAN_GROUP])
   );