Browse Source

ref(source-maps): Improve artifact's page header & breadcrumbs - (#45984)

Priscila Oliveira 2 years ago
parent
commit
ce8ee553cd

+ 3 - 1
static/app/components/events/contexts/gpu/index.spec.tsx

@@ -40,7 +40,9 @@ const event = {
   },
   },
 };
 };
 
 
-describe('gpu event context', function () {
+// Flaky test https://github.com/getsentry/sentry/actions/runs/4465585304/jobs/7842795315?pr=45984
+// eslint-disable-next-line
+describe.skip('gpu event context', function () {
   it('display redacted data', async function () {
   it('display redacted data', async function () {
     render(<GPUEventContext event={event} data={gpuMockData} />, {
     render(<GPUEventContext event={event} data={gpuMockData} />, {
       organization: {
       organization: {

+ 26 - 22
static/app/routes.tsx

@@ -513,18 +513,20 @@ function buildRoutes() {
             };
             };
           })}
           })}
         />
         />
-        <Route path="debug-id-bundles/">
-          <IndexRoute
-            component={make(async () => {
-              const {ProjectSourceMapsContainer} = await import(
-                'sentry/views/settings/projectSourceMaps'
-              );
-              return {
-                default: ProjectSourceMapsContainer,
-              };
-            })}
-          />
+        <Route
+          path="debug-id-bundles/"
+          name={t('Debug ID Bundles')}
+          component={make(async () => {
+            const {ProjectSourceMapsContainer} = await import(
+              'sentry/views/settings/projectSourceMaps'
+            );
+            return {
+              default: ProjectSourceMapsContainer,
+            };
+          })}
+        >
           <Route
           <Route
+            name={t('Artifact Bundle')}
             path=":bundleId/"
             path=":bundleId/"
             component={make(async () => {
             component={make(async () => {
               const {ProjectSourceMapsContainer} = await import(
               const {ProjectSourceMapsContainer} = await import(
@@ -536,18 +538,20 @@ function buildRoutes() {
             })}
             })}
           />
           />
         </Route>
         </Route>
-        <Route path="release-bundles/">
-          <IndexRoute
-            component={make(async () => {
-              const {ProjectSourceMapsContainer} = await import(
-                'sentry/views/settings/projectSourceMaps'
-              );
-              return {
-                default: ProjectSourceMapsContainer,
-              };
-            })}
-          />
+        <Route
+          path="release-bundles/"
+          name={t('Release Bundles')}
+          component={make(async () => {
+            const {ProjectSourceMapsContainer} = await import(
+              'sentry/views/settings/projectSourceMaps'
+            );
+            return {
+              default: ProjectSourceMapsContainer,
+            };
+          })}
+        >
           <Route
           <Route
+            name={t('Artifact Bundle')}
             path=":bundleId/"
             path=":bundleId/"
             component={make(async () => {
             component={make(async () => {
               const {ProjectSourceMapsContainer} = await import(
               const {ProjectSourceMapsContainer} = await import(

+ 64 - 0
static/app/views/settings/projectSourceMaps/debugIdBundlesTags.tsx

@@ -0,0 +1,64 @@
+import {Fragment} from 'react';
+import styled from '@emotion/styled';
+
+import Placeholder from 'sentry/components/placeholder';
+import Tag from 'sentry/components/tag';
+import {t, tct} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+
+type Props = {
+  dist?: string | null;
+  loading?: boolean;
+  release?: string | null;
+};
+
+export function DebugIdBundlesTags({dist, release, loading}: Props) {
+  return (
+    <Tags>
+      {loading ? (
+        <Fragment>
+          <StyledPlaceholder width="60px" height="20px" />
+          <StyledPlaceholder width="40px" height="20px" />
+        </Fragment>
+      ) : (
+        <Fragment>
+          {dist && (
+            <Tag
+              tooltipText={tct('Associated with release "[distribution]"', {
+                distribution: dist,
+              })}
+              type="info"
+            >
+              {dist}
+            </Tag>
+          )}
+          {release && (
+            <Tag
+              tooltipText={tct('Associated with release "[releaseName]"', {
+                releaseName: release,
+              })}
+              type="info"
+            >
+              {release}
+            </Tag>
+          )}
+          {!dist && !release && (
+            <Tag tooltipText={t('Not associated with a release or distribution')}>
+              {t('none')}
+            </Tag>
+          )}
+        </Fragment>
+      )}
+    </Tags>
+  );
+}
+
+const Tags = styled('div')`
+  display: flex;
+  flex-wrap: wrap;
+  gap: ${space(0.5)};
+`;
+
+const StyledPlaceholder = styled(Placeholder)`
+  background-color: ${p => p.theme.background};
+`;

+ 4 - 43
static/app/views/settings/projectSourceMaps/projectSourceMaps.tsx

@@ -19,10 +19,7 @@ import NavTabs from 'sentry/components/navTabs';
 import Pagination from 'sentry/components/pagination';
 import Pagination from 'sentry/components/pagination';
 import {PanelTable} from 'sentry/components/panels';
 import {PanelTable} from 'sentry/components/panels';
 import SearchBar from 'sentry/components/searchBar';
 import SearchBar from 'sentry/components/searchBar';
-import Tag from 'sentry/components/tag';
-import TextOverflow from 'sentry/components/textOverflow';
 import {Tooltip} from 'sentry/components/tooltip';
 import {Tooltip} from 'sentry/components/tooltip';
-import Version from 'sentry/components/version';
 import {IconArrow, IconDelete} from 'sentry/icons';
 import {IconArrow, IconDelete} from 'sentry/icons';
 import {t, tct} from 'sentry/locale';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {space} from 'sentry/styles/space';
@@ -34,6 +31,7 @@ import useOrganization from 'sentry/utils/useOrganization';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 import TextBlock from 'sentry/views/settings/components/text/textBlock';
 import TextBlock from 'sentry/views/settings/components/text/textBlock';
+import {DebugIdBundlesTags} from 'sentry/views/settings/projectSourceMaps/debugIdBundlesTags';
 
 
 enum SORT_BY {
 enum SORT_BY {
   ASC = 'date_added',
   ASC = 'date_added',
@@ -58,11 +56,7 @@ function SourceMapsTableRow({
   return (
   return (
     <Fragment>
     <Fragment>
       <IDColumn>
       <IDColumn>
-        <TextOverflow>
-          <Link to={link}>
-            <Version version={name} anchor={false} tooltipRawVersion truncate={false} />
-          </Link>
-        </TextOverflow>
+        <Link to={link}>{name}</Link>
         {idColumnDetails}
         {idColumnDetails}
       </IDColumn>
       </IDColumn>
       <ArtifactsTotalColumn>
       <ArtifactsTotalColumn>
@@ -305,35 +299,7 @@ export function ProjectSourceMaps({location, router, project}: Props) {
                   project.slug
                   project.slug
                 }/source-maps/debug-id-bundles/${encodeURIComponent(data.bundleId)}`}
                 }/source-maps/debug-id-bundles/${encodeURIComponent(data.bundleId)}`}
                 idColumnDetails={
                 idColumnDetails={
-                  <Tags>
-                    {data.dist && (
-                      <Tag
-                        tooltipText={tct('Associated with release "[distribution]"', {
-                          distribution: data.dist,
-                        })}
-                        type="info"
-                      >
-                        {data.dist}
-                      </Tag>
-                    )}
-                    {data.release && (
-                      <Tag
-                        tooltipText={tct('Associated with release "[releaseName]"', {
-                          releaseName: data.release,
-                        })}
-                        type="info"
-                      >
-                        {data.release}
-                      </Tag>
-                    )}
-                    {!data.dist && !data.release && (
-                      <Tag
-                        tooltipText={t('Not associated with a release or distribution')}
-                      >
-                        {t('none')}
-                      </Tag>
-                    )}
-                  </Tags>
+                  <DebugIdBundlesTags dist={data.dist} release={data.release} />
                 }
                 }
               />
               />
             ))
             ))
@@ -400,18 +366,13 @@ const IDColumn = styled(Column)`
   justify-content: center;
   justify-content: center;
   align-items: flex-start;
   align-items: flex-start;
   gap: ${space(0.5)};
   gap: ${space(0.5)};
+  word-break: break-word;
 `;
 `;
 
 
 const ActionsColumn = styled(Column)`
 const ActionsColumn = styled(Column)`
   justify-content: flex-end;
   justify-content: flex-end;
 `;
 `;
 
 
-const Tags = styled('div')`
-  display: flex;
-  flex-wrap: wrap;
-  gap: ${space(0.5)};
-`;
-
 const SearchBarWithMarginBottom = styled(SearchBar)`
 const SearchBarWithMarginBottom = styled(SearchBar)`
   margin-bottom: ${space(3)};
   margin-bottom: ${space(3)};
 `;
 `;

+ 36 - 9
static/app/views/settings/projectSourceMaps/projectSourceMapsArtifacts.spec.tsx

@@ -13,12 +13,27 @@ function renderReleaseBundlesMockRequests({
   projectSlug: string;
   projectSlug: string;
   empty?: boolean;
   empty?: boolean;
 }) {
 }) {
-  const files = MockApiClient.addMockResponse({
+  const sourceMaps = MockApiClient.addMockResponse({
+    url: `/projects/${orgSlug}/${projectSlug}/files/source-maps/`,
+    body: empty
+      ? []
+      : [
+          TestStubs.SourceMapArchive(),
+          TestStubs.SourceMapArchive({
+            id: 2,
+            name: 'abc',
+            fileCount: 3,
+            date: '2023-05-06T13:41:00Z',
+          }),
+        ],
+  });
+
+  const sourceMapsFiles = MockApiClient.addMockResponse({
     url: `/projects/${orgSlug}/${projectSlug}/releases/bea7335dfaebc0ca6e65a057/files/`,
     url: `/projects/${orgSlug}/${projectSlug}/releases/bea7335dfaebc0ca6e65a057/files/`,
     body: empty ? [] : [TestStubs.SourceMapArtifact()],
     body: empty ? [] : [TestStubs.SourceMapArtifact()],
   });
   });
 
 
-  return {files};
+  return {sourceMaps, sourceMapsFiles};
 }
 }
 
 
 function renderDebugIdBundlesMockRequests({
 function renderDebugIdBundlesMockRequests({
@@ -31,11 +46,16 @@ function renderDebugIdBundlesMockRequests({
   empty?: boolean;
   empty?: boolean;
 }) {
 }) {
   const artifactBundles = MockApiClient.addMockResponse({
   const artifactBundles = MockApiClient.addMockResponse({
+    url: `/projects/${orgSlug}/${projectSlug}/files/artifact-bundles/`,
+    body: empty ? [] : TestStubs.SourceMapsDebugIDBundles(),
+  });
+
+  const artifactBundlesFiles = MockApiClient.addMockResponse({
     url: `/projects/${orgSlug}/${projectSlug}/artifact-bundles/7227e105-744e-4066-8c69-3e5e344723fc/files/`,
     url: `/projects/${orgSlug}/${projectSlug}/artifact-bundles/7227e105-744e-4066-8c69-3e5e344723fc/files/`,
     body: empty ? [] : TestStubs.SourceMapsDebugIDBundlesArtifacts(),
     body: empty ? [] : TestStubs.SourceMapsDebugIDBundlesArtifacts(),
   });
   });
 
 
-  return {artifactBundles};
+  return {artifactBundlesFiles, artifactBundles};
 }
 }
 
 
 describe('ProjectSourceMapsArtifacts', function () {
 describe('ProjectSourceMapsArtifacts', function () {
@@ -79,9 +99,9 @@ describe('ProjectSourceMapsArtifacts', function () {
       );
       );
 
 
       // Title
       // Title
-      expect(screen.getByRole('heading')).toHaveTextContent(
-        'Release Artifact (bea7335dfaebc0ca6e65a057)'
-      );
+      expect(screen.getByRole('heading')).toHaveTextContent('Artifact Bundle');
+      // Subtitle
+      expect(screen.getByText('bea7335dfaebc0ca6e65a057')).toBeInTheDocument();
 
 
       // Search bar
       // Search bar
       expect(screen.getByPlaceholderText('Filter by Path')).toBeInTheDocument();
       expect(screen.getByPlaceholderText('Filter by Path')).toBeInTheDocument();
@@ -187,9 +207,16 @@ describe('ProjectSourceMapsArtifacts', function () {
       );
       );
 
 
       // Title
       // Title
-      expect(screen.getByRole('heading')).toHaveTextContent(
-        'Debug Id Bundle Artifact (7227e105-744e-4066-8c69-3e5e344723fc)'
-      );
+      expect(screen.getByRole('heading')).toHaveTextContent('Artifact Bundle');
+      // Subtitle
+      expect(
+        screen.getByText('7227e105-744e-4066-8c69-3e5e344723fc')
+      ).toBeInTheDocument();
+      // Chip
+      await userEvent.hover(await screen.findByText('none'));
+      expect(
+        await screen.findByText('Not associated with a release or distribution')
+      ).toBeInTheDocument();
 
 
       // Search bar
       // Search bar
       expect(screen.getByPlaceholderText('Filter by Path or ID')).toBeInTheDocument();
       expect(screen.getByPlaceholderText('Filter by Path or ID')).toBeInTheDocument();

+ 48 - 34
static/app/views/settings/projectSourceMaps/projectSourceMapsArtifacts.tsx

@@ -10,20 +10,19 @@ import Pagination from 'sentry/components/pagination';
 import {PanelTable} from 'sentry/components/panels';
 import {PanelTable} from 'sentry/components/panels';
 import SearchBar from 'sentry/components/searchBar';
 import SearchBar from 'sentry/components/searchBar';
 import Tag from 'sentry/components/tag';
 import Tag from 'sentry/components/tag';
-import TextOverflow from 'sentry/components/textOverflow';
 import TimeSince from 'sentry/components/timeSince';
 import TimeSince from 'sentry/components/timeSince';
 import {Tooltip} from 'sentry/components/tooltip';
 import {Tooltip} from 'sentry/components/tooltip';
-import Version from 'sentry/components/version';
 import {IconClock, IconDownload} from 'sentry/icons';
 import {IconClock, IconDownload} from 'sentry/icons';
 import {t, tct} from 'sentry/locale';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {space} from 'sentry/styles/space';
-import {Artifact, DebugIdBundleArtifact, Project} from 'sentry/types';
+import {Artifact, DebugIdBundle, DebugIdBundleArtifact, Project} from 'sentry/types';
 import {useQuery} from 'sentry/utils/queryClient';
 import {useQuery} from 'sentry/utils/queryClient';
 import {decodeScalar} from 'sentry/utils/queryString';
 import {decodeScalar} from 'sentry/utils/queryString';
 import useApi from 'sentry/utils/useApi';
 import useApi from 'sentry/utils/useApi';
 import useOrganization from 'sentry/utils/useOrganization';
 import useOrganization from 'sentry/utils/useOrganization';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
+import {DebugIdBundlesTags} from 'sentry/views/settings/projectSourceMaps/debugIdBundlesTags';
 
 
 enum DebugIdBundleArtifactType {
 enum DebugIdBundleArtifactType {
   INVALID = 0,
   INVALID = 0,
@@ -117,9 +116,10 @@ export function ProjectSourceMapsArtifacts({params, location, router, project}:
   const artifactsEndpoint = `/projects/${organization.slug}/${
   const artifactsEndpoint = `/projects/${organization.slug}/${
     project.slug
     project.slug
   }/releases/${encodeURIComponent(params.bundleId)}/files/`;
   }/releases/${encodeURIComponent(params.bundleId)}/files/`;
-  const debugIdBundlesEndpoint = `/projects/${organization.slug}/${
+  const debugIdBundlesArtifactsEndpoint = `/projects/${organization.slug}/${
     project.slug
     project.slug
   }/artifact-bundles/${encodeURIComponent(params.bundleId)}/files/`;
   }/artifact-bundles/${encodeURIComponent(params.bundleId)}/files/`;
+  const debugIdBundlesEndpoint = `/projects/${organization.slug}/${project.slug}/files/artifact-bundles/`;
 
 
   // debug id bundles tab url
   // debug id bundles tab url
   const debugIdsUrl = normalizeUrl(
   const debugIdsUrl = normalizeUrl(
@@ -150,24 +150,38 @@ export function ProjectSourceMapsArtifacts({params, location, router, project}:
     }
     }
   );
   );
 
 
+  const {data: debugIdBundlesArtifactsData, isLoading: debugIdBundlesArtifactsLoading} =
+    useQuery<[DebugIdBundleArtifact[], any, any]>(
+      [
+        debugIdBundlesArtifactsEndpoint,
+        {
+          query: {query, cursor},
+        },
+      ],
+      () => {
+        return api.requestPromise(debugIdBundlesArtifactsEndpoint, {
+          query: {query, cursor},
+          includeAllArgs: true,
+        });
+      },
+      {
+        staleTime: 0,
+        keepPreviousData: true,
+        enabled: tabDebugIdBundlesActive,
+      }
+    );
+
   const {data: debugIdBundlesData, isLoading: debugIdBundlesLoading} = useQuery<
   const {data: debugIdBundlesData, isLoading: debugIdBundlesLoading} = useQuery<
-    [DebugIdBundleArtifact[], any, any]
+    DebugIdBundle[]
   >(
   >(
     [
     [
       debugIdBundlesEndpoint,
       debugIdBundlesEndpoint,
       {
       {
-        query: {query, cursor},
+        query: {query: params.bundleId},
       },
       },
     ],
     ],
-    () => {
-      return api.requestPromise(debugIdBundlesEndpoint, {
-        query: {query, cursor},
-        includeAllArgs: true,
-      });
-    },
     {
     {
       staleTime: 0,
       staleTime: 0,
-      keepPreviousData: true,
       enabled: tabDebugIdBundlesActive,
       enabled: tabDebugIdBundlesActive,
     }
     }
   );
   );
@@ -185,22 +199,18 @@ export function ProjectSourceMapsArtifacts({params, location, router, project}:
   return (
   return (
     <Fragment>
     <Fragment>
       <SettingsPageHeader
       <SettingsPageHeader
-        title={
-          <Title>
-            {tabDebugIdBundlesActive
-              ? t('Debug Id Bundle Artifact')
-              : t('Release Artifact')}
-            {' ('}
-            <TextOverflow>
-              <Version
-                version={params.bundleId}
-                tooltipRawVersion
-                anchor={false}
-                truncate
+        title={t('Artifact Bundle')}
+        subtitle={
+          <VersionAndDetails>
+            {params.bundleId}
+            {tabDebugIdBundlesActive && (
+              <DebugIdBundlesTags
+                dist={debugIdBundlesData?.[0]?.dist}
+                release={debugIdBundlesData?.[0]?.release}
+                loading={debugIdBundlesLoading}
               />
               />
-            </TextOverflow>
-            {')'}
-          </Title>
+            )}
+          </VersionAndDetails>
         }
         }
       />
       />
       <SearchBarWithMarginBottom
       <SearchBarWithMarginBottom
@@ -225,14 +235,16 @@ export function ProjectSourceMapsArtifacts({params, location, router, project}:
         }
         }
         isEmpty={
         isEmpty={
           (tabDebugIdBundlesActive
           (tabDebugIdBundlesActive
-            ? debugIdBundlesData?.[0] ?? []
+            ? debugIdBundlesArtifactsData?.[0] ?? []
             : artifactsData?.[0] ?? []
             : artifactsData?.[0] ?? []
           ).length === 0
           ).length === 0
         }
         }
-        isLoading={tabDebugIdBundlesActive ? debugIdBundlesLoading : artifactsLoading}
+        isLoading={
+          tabDebugIdBundlesActive ? debugIdBundlesArtifactsLoading : artifactsLoading
+        }
       >
       >
         {tabDebugIdBundlesActive
         {tabDebugIdBundlesActive
-          ? debugIdBundlesData?.[0].map(data => {
+          ? debugIdBundlesArtifactsData?.[0].map(data => {
               const downloadUrl = `${api.baseUrl}/projects/${organization.slug}/${
               const downloadUrl = `${api.baseUrl}/projects/${organization.slug}/${
                 project.slug
                 project.slug
               }/artifact-bundles/${encodeURIComponent(params.bundleId)}/files/${
               }/artifact-bundles/${encodeURIComponent(params.bundleId)}/files/${
@@ -292,7 +304,7 @@ export function ProjectSourceMapsArtifacts({params, location, router, project}:
       <Pagination
       <Pagination
         pageLinks={
         pageLinks={
           tabDebugIdBundlesActive
           tabDebugIdBundlesActive
-            ? debugIdBundlesData?.[2]?.getResponseHeader('Link') ?? ''
+            ? debugIdBundlesArtifactsData?.[2]?.getResponseHeader('Link') ?? ''
             : artifactsData?.[2]?.getResponseHeader('Link') ?? ''
             : artifactsData?.[2]?.getResponseHeader('Link') ?? ''
         }
         }
       />
       />
@@ -366,7 +378,9 @@ const DebugIdAndFileTypeWrapper = styled('div')`
   color: ${p => p.theme.subText};
   color: ${p => p.theme.subText};
 `;
 `;
 
 
-const Title = styled('div')`
+const VersionAndDetails = styled('div')`
   display: flex;
   display: flex;
-  align-items: center;
+  flex-direction: column;
+  gap: ${space(1)};
+  word-break: break-word;
 `;
 `;