Skip to content

Commit 0cbc994

Browse files
jennmuengandrewshie-sentry
authored andcommitted
feat(autofix): Split out code mappings into a separate setup step (#81083)
Renders the guided steps UI again, but this time the code mappings is its own step. To aid in reducing friction, there are now prominent buttons that jump to the relevant settings page: ![CleanShot 2024-11-20 at 14 30 45@2x](https://github.com/user-attachments/assets/58a69a22-2335-43da-8cda-e211a288c058) ![CleanShot 2024-11-20 at 14 30 55@2x](https://github.com/user-attachments/assets/3fe962bd-1fa2-4775-b02d-a4c354732066) The code mappings button will link directly to the code mappings setup page, but as there isn't really a straightforward way for me to easily test a github integration setup locally, I've added a fallback to just linking to the github integration setup page as usual if that query fails. The configuration query is reverse engineered from: https://github.com/getsentry/sentry/blob/99f305eab1c7afdfbcf56db3ef9377b4cceb5e17/static/app/views/settings/organizationIntegrations/integrationDetailedView.tsx#L42-L66
1 parent 679e936 commit 0cbc994

File tree

3 files changed

+167
-76
lines changed

3 files changed

+167
-76
lines changed

static/app/components/events/autofix/autofixSetupModal.spec.tsx

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,96 @@ describe('AutofixSetupContent', function () {
4040
},
4141
});
4242

43+
MockApiClient.addMockResponse({
44+
url: '/organizations/org-slug/integrations/?provider_key=github&includeConfig=0',
45+
body: [],
46+
});
47+
4348
render(<AutofixSetupContent groupId="1" projectId="1" />);
4449

4550
expect(await screen.findByText('Install the GitHub Integration')).toBeInTheDocument();
51+
expect(screen.getByText(/Install the GitHub integration/)).toBeInTheDocument();
52+
});
53+
54+
it('renders the code mappings instructions', async function () {
55+
MockApiClient.addMockResponse({
56+
url: '/issues/1/autofix/setup/',
57+
body: {
58+
genAIConsent: {ok: true},
59+
integration: {ok: false, reason: 'integration_no_code_mappings'},
60+
githubWriteIntegration: {
61+
ok: false,
62+
repos: [
63+
{
64+
provider: 'integrations:github',
65+
owner: 'getsentry',
66+
name: 'sentry',
67+
external_id: '123',
68+
ok: false,
69+
},
70+
],
71+
},
72+
},
73+
});
74+
75+
MockApiClient.addMockResponse({
76+
url: '/organizations/org-slug/integrations/?provider_key=github&includeConfig=0',
77+
body: [
78+
{
79+
id: '123',
80+
},
81+
],
82+
});
83+
84+
render(<AutofixSetupContent groupId="1" projectId="1" />);
85+
86+
expect(await screen.findByText('Set up Code Mappings')).toBeInTheDocument();
87+
expect(
88+
screen.getByText(
89+
/Set up code mappings for the Github repositories you want to run Autofix on/
90+
)
91+
).toBeInTheDocument();
92+
expect(
93+
screen.getByRole('button', {name: 'Configure Code Mappings'})
94+
).toBeInTheDocument();
95+
});
96+
97+
it('renders the code mappings with fallback if no integration is configured', async function () {
98+
MockApiClient.addMockResponse({
99+
url: '/issues/1/autofix/setup/',
100+
body: {
101+
genAIConsent: {ok: true},
102+
integration: {ok: false, reason: 'integration_no_code_mappings'},
103+
githubWriteIntegration: {
104+
ok: false,
105+
repos: [
106+
{
107+
provider: 'integrations:github',
108+
owner: 'getsentry',
109+
name: 'sentry',
110+
external_id: '123',
111+
ok: false,
112+
},
113+
],
114+
},
115+
},
116+
});
117+
118+
MockApiClient.addMockResponse({
119+
url: '/organizations/org-slug/integrations/?provider_key=github&includeConfig=0',
120+
body: [],
121+
});
122+
123+
render(<AutofixSetupContent groupId="1" projectId="1" />);
124+
125+
expect(await screen.findByText('Set up Code Mappings')).toBeInTheDocument();
126+
expect(
127+
screen.getByText(
128+
/Set up code mappings for the Github repositories you want to run Autofix on/
129+
)
130+
).toBeInTheDocument();
46131
expect(
47-
screen.getByText(/Install the GitHub integration by navigating to/)
132+
screen.getByRole('button', {name: 'Configure Integration'})
48133
).toBeInTheDocument();
49134
});
50135
});

static/app/components/events/autofix/autofixSetupModal.tsx

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import {Fragment, useEffect} from 'react';
22
import styled from '@emotion/styled';
33

4+
import {Button} from 'sentry/components/button';
45
import {
56
type AutofixSetupRepoDefinition,
67
type AutofixSetupResponse,
78
useAutofixSetup,
89
} from 'sentry/components/events/autofix/useAutofixSetup';
10+
import {GuidedSteps} from 'sentry/components/guidedSteps/guidedSteps';
911
import ExternalLink from 'sentry/components/links/externalLink';
1012
import LoadingError from 'sentry/components/loadingError';
1113
import LoadingIndicator from 'sentry/components/loadingIndicator';
1214
import {IconCheckmark, IconGithub} from 'sentry/icons';
1315
import {t, tct} from 'sentry/locale';
1416
import {space} from 'sentry/styles/space';
17+
import type {Integration} from 'sentry/types/integrations';
1518
import {trackAnalytics} from 'sentry/utils/analytics';
19+
import {useApiQuery} from 'sentry/utils/queryClient';
1620
import useOrganization from 'sentry/utils/useOrganization';
1721

1822
function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupResponse}) {
@@ -22,6 +26,9 @@ function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupRespo
2226
{tct('The GitHub integration is already installed, [link: view in settings].', {
2327
link: <ExternalLink href={`/settings/integrations/github/`} />,
2428
})}
29+
<GuidedSteps.ButtonWrapper>
30+
<GuidedSteps.NextButton />
31+
</GuidedSteps.ButtonWrapper>
2532
</Fragment>
2633
);
2734
}
@@ -47,43 +54,23 @@ function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupRespo
4754
}
4855
)}
4956
</p>
50-
</Fragment>
51-
);
52-
}
53-
54-
if (autofixSetup.integration.reason === 'integration_no_code_mappings') {
55-
return (
56-
<Fragment>
57-
<p>
58-
{tct(
59-
'You have an active GitHub installation, but no code mappings for this project. Add code mappings by visiting the [link:integration settings page] and editing your configuration.',
60-
{
61-
link: <ExternalLink href={`/settings/integrations/github/`} />,
62-
}
63-
)}
64-
</p>
65-
<p>
66-
{tct(
67-
'Once added, come back to this page. For more information related to installing the GitHub integration, read the [link:documentation].',
68-
{
69-
link: (
70-
<ExternalLink href="https://docs.sentry.io/product/integrations/source-code-mgmt/github/" />
71-
),
72-
}
73-
)}
74-
</p>
57+
<GuidedSteps.ButtonWrapper>
58+
<ExternalLink href="/settings/integrations/github/">
59+
<Button size="sm" priority="primary">
60+
{t('Set up Integration')}
61+
</Button>
62+
</ExternalLink>
63+
<GuidedSteps.NextButton />
64+
</GuidedSteps.ButtonWrapper>
7565
</Fragment>
7666
);
7767
}
7868

7969
return (
8070
<Fragment>
8171
<p>
82-
{tct(
83-
'Install the GitHub integration by navigating to the [link:integration settings page] and clicking the "Install" button. Follow the steps provided.',
84-
{
85-
link: <ExternalLink href={`/settings/integrations/github/`} />,
86-
}
72+
{t(
73+
'Install the GitHub integration on the integration settings page and clicking the "Install" button. Follow the steps provided.'
8774
)}
8875
</p>
8976
<p>
@@ -96,6 +83,47 @@ function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupRespo
9683
}
9784
)}
9885
</p>
86+
<GuidedSteps.ButtonWrapper>
87+
<ExternalLink href="/settings/integrations/github/">
88+
<Button size="sm" priority="primary">
89+
{t('Set up Integration')}
90+
</Button>
91+
</ExternalLink>
92+
<GuidedSteps.NextButton />
93+
</GuidedSteps.ButtonWrapper>
94+
</Fragment>
95+
);
96+
}
97+
98+
function AutofixCodeMappingStep() {
99+
const organization = useOrganization();
100+
const {data: integrationConfigurations} = useApiQuery<Integration[]>(
101+
[
102+
`/organizations/${organization.slug}/integrations/?provider_key=github&includeConfig=0`,
103+
],
104+
{
105+
staleTime: Infinity,
106+
}
107+
);
108+
109+
const configurationId = integrationConfigurations?.at(0)?.id;
110+
const url = `/settings/integrations/github/${configurationId ? configurationId + '/?tab=codeMappings' : ''}`;
111+
112+
return (
113+
<Fragment>
114+
<p>
115+
{t(
116+
'Set up code mappings for the Github repositories you want to run Autofix on for this project.'
117+
)}
118+
</p>
119+
<GuidedSteps.ButtonWrapper>
120+
<GuidedSteps.BackButton />
121+
<ExternalLink href={url}>
122+
<Button size="sm" priority="primary">
123+
{configurationId ? t('Configure Code Mappings') : t('Configure Integration')}
124+
</Button>
125+
</ExternalLink>
126+
</GuidedSteps.ButtonWrapper>
99127
</Fragment>
100128
);
101129
}
@@ -122,34 +150,27 @@ export function GitRepoLink({repo}: {repo: AutofixSetupRepoDefinition}) {
122150
);
123151
}
124152

125-
function SetupStep({
126-
title,
127-
isCompleted,
128-
children,
129-
}: {
130-
children: React.ReactNode;
131-
isCompleted: boolean;
132-
title: string;
133-
}) {
134-
return (
135-
<StepWrapper>
136-
<StepHeader>
137-
<StepTitle>{title}</StepTitle>
138-
{isCompleted && <IconCheckmark color="success" size="sm" />}
139-
</StepHeader>
140-
<div>{children}</div>
141-
</StepWrapper>
142-
);
143-
}
144-
145153
function AutofixSetupSteps({autofixSetup}: {autofixSetup: AutofixSetupResponse}) {
146154
return (
147-
<SetupStep
148-
title={t('Install the GitHub Integration')}
149-
isCompleted={autofixSetup.integration.ok}
150-
>
151-
<AutofixIntegrationStep autofixSetup={autofixSetup} />
152-
</SetupStep>
155+
<GuidedSteps>
156+
<GuidedSteps.Step
157+
stepKey="integration"
158+
title={t('Install the GitHub Integration')}
159+
isCompleted={
160+
autofixSetup.integration.ok ||
161+
autofixSetup.integration.reason === 'integration_no_code_mappings'
162+
}
163+
>
164+
<AutofixIntegrationStep autofixSetup={autofixSetup} />
165+
</GuidedSteps.Step>
166+
<GuidedSteps.Step
167+
stepKey="codeMappings"
168+
title={t('Set up Code Mappings')}
169+
isCompleted={autofixSetup.integration.ok}
170+
>
171+
<AutofixCodeMappingStep />
172+
</GuidedSteps.Step>
173+
</GuidedSteps>
153174
);
154175
}
155176

@@ -237,22 +258,3 @@ const Divider = styled('div')`
237258
margin: ${space(3)} 0;
238259
border-bottom: 2px solid ${p => p.theme.gray100};
239260
`;
240-
241-
const StepWrapper = styled('div')`
242-
margin-top: ${space(3)};
243-
padding-left: ${space(2)};
244-
border-left: 2px solid ${p => p.theme.border};
245-
`;
246-
247-
const StepHeader = styled('div')`
248-
display: flex;
249-
align-items: center;
250-
gap: ${space(1)};
251-
margin-bottom: ${space(1)};
252-
`;
253-
254-
const StepTitle = styled('p')`
255-
font-size: ${p => p.theme.fontSizeMedium};
256-
font-weight: 600;
257-
margin: 0;
258-
`;

static/app/views/issueDetails/streamline/solutionsHubDrawer.spec.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ describe('SolutionsHubDrawer', () => {
196196
url: `/issues/${mockGroup.id}/autofix/`,
197197
body: {autofix: null},
198198
});
199+
MockApiClient.addMockResponse({
200+
url: '/organizations/org-slug/integrations/?provider_key=github&includeConfig=0',
201+
body: [],
202+
});
199203

200204
render(
201205
<SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,

0 commit comments

Comments
 (0)