Skip to content

Commit bc6b03f

Browse files
michellewzhangevanh
authored andcommitted
feat(flags): add python onboarding sidebar (#80871)
## what does this pr do? - this pr implements part of the feature flag onboarding sidebar specified in these [designs](https://www.figma.com/design/6w2ju2MlyuGRLKm2PKpotD/Specs%3A-Feature-Flag-Onboarding?node-id=0-1&node-type=canvas&t=UPuG14gRVUNLLQvo-0). - relates to getsentry/team-replay#502. - currently, only the python onboarding is done, as i want to get feedback on how i've structured it before doing javascript. (javascript & python are the only two SDKs we'll do for now.) - deletes the setup integration modal introduced in #80437, which is no longer needed. ## flow walkthrough The sidebar is triggered by the "set up integration" button on the feature flags table on issue details. <img width="686" alt="SCR-20241115-nhtq" src="https://github.com/user-attachments/assets/a4fecf37-a2b1-4795-aa11-f612035792c3"> It opens a flyout sidebar, which explains onboarding instructions for how to set up feature flag integrations. Depending on whether OpenFeature is selected (which allows for an additional dropdown to pick another provider) or another SDK, the instructions will vary. ### LD: <img width="474" alt="SCR-20241115-njpv" src="https://github.com/user-attachments/assets/6a638bd4-a9ac-4fb1-8646-3a55362fb6b3"> ### OpenFeature with LD: <img width="464" alt="SCR-20241115-nkwd" src="https://github.com/user-attachments/assets/622fe30d-5a66-4638-9314-de621c9f1578"> ### signing secret At the bottom of the sidebar is a spot to post a signing secret, which is what we use to verify the integration. This posts to the `/signing-secret/` endpoint: https://github.com/getsentry/sentry/blob/9dfbc5a6629127614e04b8e95b9b280ccd8e1ea1/src/sentry/api/urls.py#L2072-L2076 https://github.com/user-attachments/assets/a94c92a6-0b46-465e-b805-b1bef2f43460 when the token is saved, a success banner appears: <img width="459" alt="SCR-20241115-nmxv" src="https://github.com/user-attachments/assets/73c5841f-52d8-494d-a905-81dfe1b8cbec"> ## video demos triggering from issue details: https://github.com/user-attachments/assets/06505803-ab2f-477d-a053-2269b9af1be7 example of what it would look like if OpenFeature had multiple options: https://github.com/user-attachments/assets/5893ac06-b1f3-4925-b124-b609440c6e33 ## todo (this PR): - [x] fix/test the secret endpoint - [x] fill in links for docs ## todo (followups) - javascript onboarding - figure out CTA details (another way the sidebar can be triggered) - if we detect that `event.contexts.flags` is not undefined, we should hide the first half of the onboarding as specified in the designs - analytics charts
1 parent 2490aff commit bc6b03f

File tree

14 files changed

+769
-233
lines changed

14 files changed

+769
-233
lines changed

static/app/components/events/featureFlags/eventFeatureFlagList.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {Fragment, useCallback, useEffect, useMemo, useRef, useState} from 'react';
22
import styled from '@emotion/styled';
33

4-
import {openModal} from 'sentry/actionCreators/modal';
54
import {Button} from 'sentry/components/button';
65
import ButtonBar from 'sentry/components/buttonBar';
76
import EmptyStateWarning from 'sentry/components/emptyStateWarning';
@@ -11,10 +10,7 @@ import {
1110
FeatureFlagDrawer,
1211
} from 'sentry/components/events/featureFlags/featureFlagDrawer';
1312
import FeatureFlagSort from 'sentry/components/events/featureFlags/featureFlagSort';
14-
import {
15-
modalCss,
16-
SetupIntegrationModal,
17-
} from 'sentry/components/events/featureFlags/setupIntegrationModal';
13+
import {useFeatureFlagOnboarding} from 'sentry/components/events/featureFlags/useFeatureFlagOnboarding';
1814
import {
1915
FlagControlOptions,
2016
OrderBy,
@@ -80,6 +76,7 @@ export function EventFeatureFlagList({
8076
statsPeriod: eventView.statsPeriod,
8177
},
8278
});
79+
const {activateSidebar} = useFeatureFlagOnboarding();
8380

8481
const {
8582
suspectFlags,
@@ -95,13 +92,6 @@ export function EventFeatureFlagList({
9592
const hasFlagContext = !!event.contexts.flags;
9693
const hasFlags = Boolean(hasFlagContext && event?.contexts?.flags?.values.length);
9794

98-
function handleSetupButtonClick() {
99-
trackAnalytics('flags.setup_modal_opened', {organization});
100-
openModal(modalProps => <SetupIntegrationModal {...modalProps} />, {
101-
modalCss,
102-
});
103-
}
104-
10595
const suspectFlagNames: Set<string> = useMemo(() => {
10696
return isSuspectError || isSuspectPending
10797
? new Set()
@@ -195,7 +185,7 @@ export function EventFeatureFlagList({
195185
<Button
196186
aria-label={t('Set Up Integration')}
197187
size="xs"
198-
onClick={handleSetupButtonClick}
188+
onClick={activateSidebar}
199189
>
200190
{t('Set Up Integration')}
201191
</Button>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import {useMemo} from 'react';
2+
import styled from '@emotion/styled';
3+
4+
import OnboardingIntegrationSection from 'sentry/components/events/featureFlags/onboardingIntegrationSection';
5+
import {AuthTokenGeneratorProvider} from 'sentry/components/onboarding/gettingStartedDoc/authTokenGenerator';
6+
import type {OnboardingLayoutProps} from 'sentry/components/onboarding/gettingStartedDoc/onboardingLayout';
7+
import {Step} from 'sentry/components/onboarding/gettingStartedDoc/step';
8+
import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/types';
9+
import {useSourcePackageRegistries} from 'sentry/components/onboarding/gettingStartedDoc/useSourcePackageRegistries';
10+
import {useUrlPlatformOptions} from 'sentry/components/onboarding/platformOptionsControl';
11+
import ConfigStore from 'sentry/stores/configStore';
12+
import {useLegacyStore} from 'sentry/stores/useLegacyStore';
13+
import useApi from 'sentry/utils/useApi';
14+
import useOrganization from 'sentry/utils/useOrganization';
15+
16+
interface FeatureFlagOnboardingLayoutProps extends OnboardingLayoutProps {
17+
integration?: string;
18+
provider?: string;
19+
}
20+
21+
export function FeatureFlagOnboardingLayout({
22+
docsConfig,
23+
dsn,
24+
platformKey,
25+
projectId,
26+
projectSlug,
27+
projectKeyId,
28+
configType = 'onboarding',
29+
integration = '',
30+
provider = '',
31+
}: FeatureFlagOnboardingLayoutProps) {
32+
const api = useApi();
33+
const organization = useOrganization();
34+
const {isPending: isLoadingRegistry, data: registryData} =
35+
useSourcePackageRegistries(organization);
36+
const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions);
37+
const {isSelfHosted, urlPrefix} = useLegacyStore(ConfigStore);
38+
39+
const {steps} = useMemo(() => {
40+
const doc = docsConfig[configType] ?? docsConfig.onboarding;
41+
42+
const docParams: DocsParams<any> = {
43+
api,
44+
projectKeyId,
45+
dsn,
46+
organization,
47+
platformKey,
48+
projectId,
49+
projectSlug,
50+
isFeedbackSelected: false,
51+
isPerformanceSelected: false,
52+
isProfilingSelected: false,
53+
isReplaySelected: false,
54+
sourcePackageRegistries: {
55+
isLoading: isLoadingRegistry,
56+
data: registryData,
57+
},
58+
platformOptions: selectedOptions,
59+
isSelfHosted,
60+
urlPrefix,
61+
featureFlagOptions: {
62+
integration,
63+
},
64+
};
65+
66+
return {
67+
steps: [...doc.install(docParams), ...doc.configure(docParams)],
68+
};
69+
}, [
70+
docsConfig,
71+
dsn,
72+
isLoadingRegistry,
73+
organization,
74+
platformKey,
75+
projectId,
76+
projectSlug,
77+
registryData,
78+
selectedOptions,
79+
configType,
80+
urlPrefix,
81+
isSelfHosted,
82+
api,
83+
projectKeyId,
84+
integration,
85+
]);
86+
87+
return (
88+
<AuthTokenGeneratorProvider projectSlug={projectSlug}>
89+
<Wrapper>
90+
<Steps>
91+
{steps.map(step => (
92+
<Step key={step.title ?? step.type} {...step} />
93+
))}
94+
</Steps>
95+
<OnboardingIntegrationSection provider={provider} integration={integration} />
96+
</Wrapper>
97+
</AuthTokenGeneratorProvider>
98+
);
99+
}
100+
101+
const Steps = styled('div')`
102+
display: flex;
103+
flex-direction: column;
104+
gap: 1.5rem;
105+
`;
106+
107+
const Wrapper = styled('div')`
108+
h4 {
109+
margin-bottom: 0.5em;
110+
}
111+
&& {
112+
p {
113+
margin-bottom: 0;
114+
}
115+
h5 {
116+
margin-bottom: 0;
117+
}
118+
}
119+
`;

0 commit comments

Comments
 (0)