Browse Source

feat(sampling): Add incompatible SDKs alert [TET-304] (#37535)

Matej Minar 2 years ago
parent
commit
343d6ce368

+ 8 - 3
static/app/actionCreators/serverSideSampling.tsx

@@ -8,15 +8,20 @@ import handleXhrErrorResponse from 'sentry/utils/handleXhrErrorResponse';
 export function fetchSamplingSdkVersions({
   api,
   orgSlug,
+  projectID,
 }: {
   api: Client;
   orgSlug: Organization['slug'];
+  projectID: Project['id'];
 }): Promise<SamplingSdkVersion[]> {
   const {samplingDistribution} = ServerSideSamplingStore.getState();
 
-  const projectIds = samplingDistribution.project_breakdown?.map(
-    projectBreakdown => projectBreakdown.project_id
-  );
+  const projectIds = [
+    projectID,
+    ...(samplingDistribution.project_breakdown?.map(
+      projectBreakdown => projectBreakdown.project_id
+    ) ?? []),
+  ];
 
   const promise = api.requestPromise(
     `/organizations/${orgSlug}/dynamic-sampling/sdk-versions/`,

+ 1 - 0
static/app/types/sampling.tsx

@@ -124,6 +124,7 @@ export type SamplingDistribution = {
 export type SamplingSdkVersion = {
   isSendingSampleRate: boolean;
   isSendingSource: boolean;
+  isSupportedPlatform: boolean;
   latestSDKName: string;
   latestSDKVersion: string;
   project: string;

+ 4 - 0
static/app/utils/analytics/samplingAnalyticsEvents.tsx

@@ -11,6 +11,9 @@ export type SamplingEventParameters = {
   'sampling.sdk.client.rate.change.alert': {
     project_id: string;
   };
+  'sampling.sdk.incompatible.alert': {
+    project_id: string;
+  };
   'sampling.sdk.updgrades.alert': {
     project_id: string;
   };
@@ -104,6 +107,7 @@ type SamplingAnalyticsKey = keyof SamplingEventParameters;
 export const samplingEventMap: Record<SamplingAnalyticsKey, string> = {
   'sampling.sdk.client.rate.change.alert': 'Recommended sdk client rate change alert',
   'sampling.sdk.updgrades.alert': 'Recommended sdk upgrades alert',
+  'sampling.sdk.incompatible.alert': 'Incompatible sdk upgrades alert',
   'sampling.settings.modal.recommended.next.steps_back': 'Go back to uniform rate step',
   'sampling.settings.modal.recommended.next.steps_cancel':
     'Cancel at recommended next steps step ',

+ 2 - 1
static/app/views/settings/project/server-side-sampling/modals/uniformRateModal.tsx

@@ -102,7 +102,7 @@ export function UniformRateModal({
     groupBy: useMemo(() => ['outcome', 'reason'], []),
   });
 
-  const {recommendedSdkUpgrades} = useRecommendedSdkUpgrades({
+  const {recommendedSdkUpgrades, incompatibleProjects} = useRecommendedSdkUpgrades({
     orgSlug: organization.slug,
   });
 
@@ -522,6 +522,7 @@ export function UniformRateModal({
             projectId={project.id}
             rules={rules}
             recommendedSdkUpgrades={recommendedSdkUpgrades}
+            incompatibleProjects={incompatibleProjects}
             showLinkToTheModal={false}
             onReadDocs={onReadDocs}
           />

+ 1 - 1
static/app/views/settings/project/server-side-sampling/samplingSDKClientRateChangeAlert.tsx

@@ -48,7 +48,7 @@ export function SamplingSDKClientRateChangeAlert({
       showIcon
       trailingItems={
         <Button
-          href={`${SERVER_SIDE_SAMPLING_DOC_LINK}product/data-management-settings/server-side-sampling/getting-started/#4-increase-your-sdk-transaction-sample-rate`}
+          href={`${SERVER_SIDE_SAMPLING_DOC_LINK}getting-started/#4-increase-your-sdk-transaction-sample-rate`}
           onClick={onReadDocs}
           priority="link"
           borderless

+ 80 - 35
static/app/views/settings/project/server-side-sampling/samplingSDKUpgradesAlert.tsx

@@ -1,4 +1,4 @@
-import {useEffect} from 'react';
+import {Fragment, useEffect} from 'react';
 import styled from '@emotion/styled';
 
 import {openModal} from 'sentry/actionCreators/modal';
@@ -7,7 +7,7 @@ import Button from 'sentry/components/button';
 import ProjectBadge from 'sentry/components/idBadge/projectBadge';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {Organization} from 'sentry/types';
+import {Organization, Project} from 'sentry/types';
 import {RecommendedSdkUpgrade, SamplingRule} from 'sentry/types/sampling';
 import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
 
@@ -15,9 +15,10 @@ import {
   RecommendedStepsModal,
   RecommendedStepsModalProps,
 } from './modals/recommendedStepsModal';
-import {isUniformRule} from './utils';
+import {isUniformRule, SERVER_SIDE_SAMPLING_DOC_LINK} from './utils';
 
 type Props = Pick<RecommendedStepsModalProps, 'projectId' | 'onReadDocs'> & {
+  incompatibleProjects: Project[];
   organization: Organization;
   recommendedSdkUpgrades: RecommendedSdkUpgrade[];
   rules: SamplingRule[];
@@ -28,22 +29,30 @@ export function SamplingSDKUpgradesAlert({
   organization,
   projectId,
   recommendedSdkUpgrades,
+  incompatibleProjects,
   rules,
   onReadDocs,
   showLinkToTheModal = true,
 }: Props) {
   useEffect(() => {
-    if (recommendedSdkUpgrades.length === 0) {
-      return;
+    if (recommendedSdkUpgrades.length > 0) {
+      trackAdvancedAnalyticsEvent('sampling.sdk.updgrades.alert', {
+        organization,
+        project_id: projectId,
+      });
     }
-
-    trackAdvancedAnalyticsEvent('sampling.sdk.updgrades.alert', {
-      organization,
-      project_id: projectId,
-    });
   }, [recommendedSdkUpgrades.length, organization, projectId]);
 
-  if (recommendedSdkUpgrades.length === 0) {
+  useEffect(() => {
+    if (incompatibleProjects.length > 0) {
+      trackAdvancedAnalyticsEvent('sampling.sdk.incompatible.alert', {
+        organization,
+        project_id: projectId,
+      });
+    }
+  }, [incompatibleProjects.length, organization, projectId]);
+
+  if (recommendedSdkUpgrades.length === 0 && incompatibleProjects.length === 0) {
     return null;
   }
 
@@ -62,31 +71,67 @@ export function SamplingSDKUpgradesAlert({
   const uniformRule = rules.find(isUniformRule);
 
   return (
-    <Alert
-      data-test-id="recommended-sdk-upgrades-alert"
-      type="info"
-      showIcon
-      trailingItems={
-        showLinkToTheModal && uniformRule ? (
-          <Button onClick={handleOpenRecommendedSteps} priority="link" borderless>
-            {t('Learn More')}
-          </Button>
-        ) : undefined
-      }
-    >
-      {t(
-        'To activate server-side sampling rules, it’s a requirement to update the following project SDK(s):'
+    <Fragment>
+      {recommendedSdkUpgrades.length > 0 && (
+        <Alert
+          data-test-id="recommended-sdk-upgrades-alert"
+          type="info"
+          showIcon
+          trailingItems={
+            showLinkToTheModal && uniformRule ? (
+              <Button onClick={handleOpenRecommendedSteps} priority="link" borderless>
+                {t('Learn More')}
+              </Button>
+            ) : undefined
+          }
+        >
+          {t(
+            'To activate server-side sampling rules, it’s a requirement to update the following project SDK(s):'
+          )}
+          <Projects>
+            {recommendedSdkUpgrades.map(recommendedSdkUpgrade => (
+              <ProjectBadge
+                key={recommendedSdkUpgrade.project.id}
+                project={recommendedSdkUpgrade.project}
+                avatarSize={16}
+              />
+            ))}
+          </Projects>
+        </Alert>
+      )}
+      {incompatibleProjects.length > 0 && (
+        <Alert
+          data-test-id="incompatible-projects-alert"
+          type="warning"
+          showIcon
+          trailingItems={
+            showLinkToTheModal ? (
+              <Button
+                href={`${SERVER_SIDE_SAMPLING_DOC_LINK}getting-started/#current-limitations`}
+                priority="link"
+                borderless
+                external
+              >
+                {t('Learn More')}
+              </Button>
+            ) : undefined
+          }
+        >
+          {t(
+            'The following projects are currently incompatible with Server-Side Sampling:'
+          )}
+          <Projects>
+            {incompatibleProjects.map(incompatibleProject => (
+              <ProjectBadge
+                key={incompatibleProject.id}
+                project={incompatibleProject}
+                avatarSize={16}
+              />
+            ))}
+          </Projects>
+        </Alert>
       )}
-      <Projects>
-        {recommendedSdkUpgrades.map(recommendedSdkUpgrade => (
-          <ProjectBadge
-            key={recommendedSdkUpgrade.project.id}
-            project={recommendedSdkUpgrade.project}
-            avatarSize={16}
-          />
-        ))}
-      </Projects>
-    </Alert>
+    </Fragment>
   );
 }
 

+ 11 - 5
static/app/views/settings/project/server-side-sampling/serverSideSampling.tsx

@@ -104,11 +104,12 @@ export function ServerSideSampling({project}: Props) {
       await fetchSamplingSdkVersions({
         orgSlug: organization.slug,
         api,
+        projectID: project.id,
       });
     }
 
     fetchRecommendedSdkUpgrades();
-  }, [api, organization.slug, project.slug, hasAccess]);
+  }, [api, organization.slug, project.slug, project.id, hasAccess]);
 
   const {projectStats} = useProjectStats({
     orgSlug: organization.slug,
@@ -118,10 +119,13 @@ export function ServerSideSampling({project}: Props) {
     groupBy: 'outcome',
   });
 
-  const {recommendedSdkUpgrades, fetching: fetchingRecommendedSdkUpgrades} =
-    useRecommendedSdkUpgrades({
-      orgSlug: organization.slug,
-    });
+  const {
+    recommendedSdkUpgrades,
+    incompatibleProjects,
+    fetching: fetchingRecommendedSdkUpgrades,
+  } = useRecommendedSdkUpgrades({
+    orgSlug: organization.slug,
+  });
 
   async function handleActivateToggle(rule: SamplingRule) {
     const newRules = rules.map(r => {
@@ -428,6 +432,7 @@ export function ServerSideSampling({project}: Props) {
             'These settings can only be edited by users with the organization owner, manager, or admin role.'
           )}
         />
+
         {!!rules.length &&
           !fetchingRecommendedSdkUpgrades &&
           (!recommendedSdkUpgrades.length ? (
@@ -444,6 +449,7 @@ export function ServerSideSampling({project}: Props) {
               rules={rules}
               recommendedSdkUpgrades={recommendedSdkUpgrades}
               onReadDocs={handleReadDocs}
+              incompatibleProjects={incompatibleProjects}
             />
           ))}
         <SamplingBreakdown orgSlug={organization.slug} />

+ 12 - 4
static/app/views/settings/project/server-side-sampling/utils/useRecommendedSdkUpgrades.tsx

@@ -12,13 +12,17 @@ export function useRecommendedSdkUpgrades({orgSlug}: Props) {
   const {samplingSdkVersions, fetching} = useLegacyStore(ServerSideSamplingStore);
 
   const sdksToUpdate = samplingSdkVersions.filter(
-    ({isSendingSource, isSendingSampleRate}) => {
-      return !isSendingSource || !isSendingSampleRate;
+    ({isSendingSource, isSendingSampleRate, isSupportedPlatform}) => {
+      return (!isSendingSource || !isSendingSampleRate) && isSupportedPlatform;
     }
   );
 
+  const incompatibleSDKs = samplingSdkVersions.filter(
+    ({isSupportedPlatform}) => !isSupportedPlatform
+  );
+
   const {projects} = useProjects({
-    slugs: sdksToUpdate.map(({project}) => project),
+    slugs: [...sdksToUpdate, ...incompatibleSDKs].map(({project}) => project),
     orgId: orgSlug,
   });
 
@@ -40,5 +44,9 @@ export function useRecommendedSdkUpgrades({orgSlug}: Props) {
     })
     .filter(defined);
 
-  return {recommendedSdkUpgrades, fetching};
+  const incompatibleProjects = projects.filter(project =>
+    incompatibleSDKs.find(incompatibleSDK => incompatibleSDK.project === project.slug)
+  );
+
+  return {recommendedSdkUpgrades, incompatibleProjects, fetching};
 }

+ 27 - 0
tests/js/spec/views/settings/project/server-side-sampling/samplingSDKUpgradesAlert.spec.tsx

@@ -18,6 +18,7 @@ describe('Server-Side Sampling - Sdk Upgrades Alert', function () {
         onReadDocs={jest.fn()}
         rules={[uniformRule]}
         recommendedSdkUpgrades={[]}
+        incompatibleProjects={[]}
         showLinkToTheModal
       />
     );
@@ -40,6 +41,7 @@ describe('Server-Side Sampling - Sdk Upgrades Alert', function () {
           rules={[uniformRule]}
           recommendedSdkUpgrades={recommendedSdkUpgrades}
           showLinkToTheModal
+          incompatibleProjects={[]}
         />
       </Fragment>
     );
@@ -71,4 +73,29 @@ describe('Server-Side Sampling - Sdk Upgrades Alert', function () {
       })
     ).toBeInTheDocument();
   });
+
+  it('renders incompatible sdks', function () {
+    const {organization, project} = getMockData();
+
+    render(
+      <SamplingSDKUpgradesAlert
+        organization={organization}
+        projectId={project.id}
+        onReadDocs={jest.fn()}
+        rules={[uniformRule]}
+        recommendedSdkUpgrades={recommendedSdkUpgrades}
+        showLinkToTheModal
+        incompatibleProjects={[TestStubs.Project({slug: 'angular', platform: 'angular'})]}
+      />
+    );
+
+    expect(screen.getByTestId('recommended-sdk-upgrades-alert')).toBeInTheDocument();
+    expect(
+      screen.getByText(
+        'The following projects are currently incompatible with Server-Side Sampling:'
+      )
+    ).toBeInTheDocument();
+
+    expect(screen.getByTestId('platform-icon-angular')).toBeInTheDocument();
+  });
 });

+ 12 - 0
tests/js/spec/views/settings/project/server-side-sampling/utils.tsx

@@ -94,6 +94,7 @@ export const mockedSamplingSdkVersions: SamplingSdkVersion[] = [
     latestSDKName: 'sentry.javascript.react',
     isSendingSampleRate: true,
     isSendingSource: true,
+    isSupportedPlatform: true,
   },
   {
     project: mockedProjects[1].slug,
@@ -101,6 +102,7 @@ export const mockedSamplingSdkVersions: SamplingSdkVersion[] = [
     latestSDKName: 'sentry.python',
     isSendingSampleRate: false,
     isSendingSource: false,
+    isSupportedPlatform: true,
   },
   {
     project: 'java',
@@ -108,6 +110,15 @@ export const mockedSamplingSdkVersions: SamplingSdkVersion[] = [
     latestSDKName: 'sentry.java',
     isSendingSampleRate: true,
     isSendingSource: false,
+    isSupportedPlatform: true,
+  },
+  {
+    project: 'angular',
+    latestSDKVersion: '1.0.2',
+    latestSDKName: 'sentry.javascript.angular',
+    isSendingSampleRate: false,
+    isSendingSource: false,
+    isSupportedPlatform: false,
   },
 ];
 
@@ -171,6 +182,7 @@ useRecommendedSdkUpgrades.mockImplementation(() => ({
       latestSDKVersion: mockedSamplingSdkVersions[1].latestSDKVersion,
     },
   ],
+  incompatibleProjects: [],
   fetching: false,
 }));
 

Some files were not shown because too many files changed in this diff