Skip to content

Commit 05d37d5

Browse files
roagaconstantinius
authored andcommitted
chore(autofix): add more copy button content (#97891)
Now copying the root cause gives: RCA + exception data Now copying the solution gives: RCA + solution plan + exception data
1 parent cd8db15 commit 05d37d5

File tree

7 files changed

+163
-58
lines changed

7 files changed

+163
-58
lines changed

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import {
1414
type CommentThread,
1515
} from 'sentry/components/events/autofix/types';
1616
import {makeAutofixQueryKey} from 'sentry/components/events/autofix/useAutofix';
17+
import {formatRootCauseWithEvent} from 'sentry/components/events/autofix/utils';
1718
import {IconArrow, IconChat, IconClose, IconCopy, IconFocus} from 'sentry/icons';
1819
import {t} from 'sentry/locale';
1920
import {space} from 'sentry/styles/space';
21+
import type {Event} from 'sentry/types/event';
2022
import {singleLineRenderer} from 'sentry/utils/marked/marked';
2123
import {useMutation, useQueryClient} from 'sentry/utils/queryClient';
2224
import testableTransition from 'sentry/utils/testableTransition';
@@ -70,6 +72,7 @@ type AutofixRootCauseProps = {
7072
rootCauseSelection: AutofixRootCauseSelection;
7173
runId: string;
7274
agentCommentThread?: CommentThread;
75+
event?: Event;
7376
isRootCauseFirstAppearance?: boolean;
7477
previousDefaultStepIndex?: number;
7578
previousInsightCount?: number;
@@ -191,7 +194,7 @@ export function formatRootCauseText(
191194
}
192195

193196
if (event.relevant_code_file) {
194-
eventParts.push(`(See ${event.relevant_code_file.file_path})`);
197+
eventParts.push(`(See @${event.relevant_code_file.file_path})`);
195198
}
196199

197200
return eventParts.join('\n');
@@ -206,11 +209,13 @@ export function formatRootCauseText(
206209
function CopyRootCauseButton({
207210
cause,
208211
customRootCause,
212+
event,
209213
}: {
210214
cause?: AutofixRootCauseData;
211215
customRootCause?: string;
216+
event?: Event;
212217
}) {
213-
const text = formatRootCauseText(cause, customRootCause);
218+
const text = formatRootCauseWithEvent(cause, customRootCause, event);
214219
const {onClick, label} = useCopyToClipboard({
215220
text,
216221
});
@@ -238,6 +243,7 @@ function AutofixRootCauseDisplay({
238243
previousDefaultStepIndex,
239244
previousInsightCount,
240245
agentCommentThread,
246+
event,
241247
}: AutofixRootCauseProps) {
242248
const cause = causes[0];
243249
const iconFocusRef = useRef<HTMLDivElement>(null);
@@ -324,7 +330,10 @@ function AutofixRootCauseDisplay({
324330
<CauseDescription>{rootCauseSelection.custom_root_cause}</CauseDescription>
325331
<BottomDivider />
326332
<BottomButtonContainer>
327-
<CopyRootCauseButton customRootCause={rootCauseSelection.custom_root_cause} />
333+
<CopyRootCauseButton
334+
customRootCause={rootCauseSelection.custom_root_cause}
335+
event={event}
336+
/>
328337
</BottomButtonContainer>
329338
</CustomRootCausePadding>
330339
</CausesContainer>
@@ -424,7 +433,7 @@ function AutofixRootCauseDisplay({
424433
</SolutionInputContainer>
425434
) : (
426435
<ButtonBar>
427-
<CopyRootCauseButton cause={cause} />
436+
<CopyRootCauseButton cause={cause} event={event} />
428437
<Button
429438
size="sm"
430439
onClick={handleMySolution}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ import {
77
} from 'sentry-test/reactTestingLibrary';
88

99
import {AutofixSolution} from 'sentry/components/events/autofix/autofixSolution';
10-
import type {AutofixSolutionTimelineEvent} from 'sentry/components/events/autofix/types';
11-
import {useAutofixRepos} from 'sentry/components/events/autofix/useAutofix';
10+
import {
11+
type AutofixData,
12+
type AutofixSolutionTimelineEvent,
13+
} from 'sentry/components/events/autofix/types';
14+
import {
15+
useAutofixData,
16+
useAutofixRepos,
17+
} from 'sentry/components/events/autofix/useAutofix';
1218

1319
jest.mock('sentry/components/events/autofix/useAutofix');
1420

@@ -45,6 +51,12 @@ describe('AutofixSolution', () => {
4551
codebases: {},
4652
});
4753

54+
jest.mocked(useAutofixData).mockReset();
55+
jest.mocked(useAutofixData).mockReturnValue({
56+
data: {} as AutofixData,
57+
isPending: false,
58+
});
59+
4860
MockApiClient.addMockResponse({
4961
url: '/organizations/org-slug/issues/123/',
5062
method: 'GET',

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ import {
1919
} from 'sentry/components/events/autofix/types';
2020
import {
2121
makeAutofixQueryKey,
22+
useAutofixData,
2223
useAutofixRepos,
2324
type AutofixResponse,
2425
} from 'sentry/components/events/autofix/useAutofix';
26+
import {formatSolutionWithEvent} from 'sentry/components/events/autofix/utils';
2527
import {Timeline} from 'sentry/components/timeline';
2628
import {IconAdd, IconChat, IconCopy, IconFix} from 'sentry/icons';
2729
import {t, tct} from 'sentry/locale';
2830
import {space} from 'sentry/styles/space';
31+
import type {Event} from 'sentry/types/event';
2932
import {trackAnalytics} from 'sentry/utils/analytics';
3033
import {singleLineRenderer} from 'sentry/utils/marked/marked';
3134
import {valueIsEqual} from 'sentry/utils/object/valueIsEqual';
@@ -119,6 +122,7 @@ type AutofixSolutionProps = {
119122
changesDisabled?: boolean;
120123
customSolution?: string;
121124
description?: string;
125+
event?: Event;
122126
isSolutionFirstAppearance?: boolean;
123127
previousDefaultStepIndex?: number;
124128
previousInsightCount?: number;
@@ -264,27 +268,27 @@ export function formatSolutionText(
264268
}
265269

266270
if (customSolution) {
267-
return `# Proposed Changes\n\n${customSolution}`;
271+
return `# Solution Plan\n\n${customSolution}`;
268272
}
269273

270274
if (!solution || solution.length === 0) {
271275
return '';
272276
}
273277

274-
const parts = ['# Proposed Changes'];
278+
const parts = ['# Solution Plan'];
275279

276280
parts.push(
277281
solution
278282
.filter(event => event.is_active)
279-
.map(event => {
280-
const eventParts = [`### ${event.title}`];
283+
.map((event, index) => {
284+
const eventParts = [`### ${index + 1}. ${event.title}`];
281285

282286
if (event.code_snippet_and_analysis) {
283287
eventParts.push(event.code_snippet_and_analysis);
284288
}
285289

286290
if (event.relevant_code_file) {
287-
eventParts.push(`(See ${event.relevant_code_file.file_path})`);
291+
eventParts.push(`(See @${event.relevant_code_file.file_path})`);
288292
}
289293

290294
return eventParts.join('\n');
@@ -298,13 +302,17 @@ export function formatSolutionText(
298302
function CopySolutionButton({
299303
solution,
300304
customSolution,
305+
event,
301306
isEditing,
307+
rootCause,
302308
}: {
303309
solution: AutofixSolutionTimelineEvent[];
304310
customSolution?: string;
311+
event?: Event;
305312
isEditing?: boolean;
313+
rootCause?: any;
306314
}) {
307-
const text = formatSolutionText(solution, customSolution);
315+
const text = formatSolutionWithEvent(solution, customSolution, event, rootCause);
308316
const {onClick, label} = useCopyToClipboard({
309317
text,
310318
});
@@ -338,12 +346,20 @@ function AutofixSolutionDisplay({
338346
customSolution,
339347
solutionSelected,
340348
agentCommentThread,
349+
event,
341350
}: Omit<AutofixSolutionProps, 'repos'>) {
342351
const organization = useOrganization();
343352
const {data: group} = useGroup({groupId});
344353
const project = group?.project;
345354

346355
const {repos} = useAutofixRepos(groupId);
356+
const {data: autofixData} = useAutofixData({groupId});
357+
358+
// Get root cause data from autofix data
359+
const rootCauseStep = autofixData?.steps?.find(
360+
step => step.type === AutofixStepType.ROOT_CAUSE_ANALYSIS
361+
);
362+
const rootCause = rootCauseStep?.causes?.[0];
347363
const {mutate: handleContinue, isPending} = useSelectSolution({groupId, runId});
348364
const [instructions, setInstructions] = useState('');
349365
const [solutionItems, setSolutionItems] = useState<AutofixSolutionTimelineEvent[]>( // This will become outdated if multiple people use it, but we can ignore this for now.
@@ -474,7 +490,12 @@ function AutofixSolutionDisplay({
474490
<BottomDivider />
475491
<BottomFooter>
476492
<div style={{flex: 1}} />
477-
<CopySolutionButton solution={solution} customSolution={customSolution} />
493+
<CopySolutionButton
494+
solution={solution}
495+
customSolution={customSolution}
496+
event={event}
497+
rootCause={rootCause}
498+
/>
478499
</BottomFooter>
479500
</CustomSolutionPadding>
480501
</SolutionContainer>
@@ -558,7 +579,7 @@ function AutofixSolutionDisplay({
558579
</InstructionsInputWrapper>
559580
</AddInstructionWrapper>
560581
<ButtonBar>
561-
<CopySolutionButton solution={solution} />
582+
<CopySolutionButton solution={solution} event={event} rootCause={rootCause} />
562583
<Tooltip
563584
isHoverable
564585
title={

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import {getAutofixRunErrorMessage} from 'sentry/components/events/autofix/utils';
2020
import {t} from 'sentry/locale';
2121
import {space} from 'sentry/styles/space';
22+
import type {Event} from 'sentry/types/event';
2223
import testableTransition from 'sentry/utils/testableTransition';
2324
import useOrganization from 'sentry/utils/useOrganization';
2425

@@ -35,6 +36,7 @@ interface StepProps {
3536
hasStepBelow: boolean;
3637
runId: string;
3738
step: AutofixStep;
39+
event?: Event;
3840
isAutoTriggeredRun?: boolean;
3941
isChangesFirstAppearance?: boolean;
4042
isRootCauseFirstAppearance?: boolean;
@@ -48,6 +50,7 @@ interface AutofixStepsProps {
4850
data: AutofixData;
4951
groupId: string;
5052
runId: string;
53+
event?: Event;
5154
}
5255

5356
function isProgressLog(
@@ -69,6 +72,7 @@ function Step({
6972
isSolutionFirstAppearance,
7073
isChangesFirstAppearance,
7174
isAutoTriggeredRun,
75+
event,
7276
}: StepProps) {
7377
return (
7478
<StepCard id={`autofix-step-${step.id}`} data-step-type={step.type}>
@@ -103,6 +107,7 @@ function Step({
103107
previousDefaultStepIndex={previousDefaultStepIndex}
104108
previousInsightCount={previousInsightCount}
105109
isRootCauseFirstAppearance={isRootCauseFirstAppearance}
110+
event={event}
106111
/>
107112
)}
108113
{step.type === AutofixStepType.SOLUTION && (
@@ -117,6 +122,7 @@ function Step({
117122
previousInsightCount={previousInsightCount}
118123
agentCommentThread={step.agent_comment_thread ?? undefined}
119124
isSolutionFirstAppearance={isSolutionFirstAppearance}
125+
event={event}
120126
/>
121127
)}
122128
{step.type === AutofixStepType.CHANGES && (
@@ -138,7 +144,7 @@ function Step({
138144
);
139145
}
140146

141-
export function AutofixSteps({data, groupId, runId}: AutofixStepsProps) {
147+
export function AutofixSteps({data, groupId, runId, event}: AutofixStepsProps) {
142148
const organization = useOrganization();
143149
const codingDisabled =
144150
organization.enableSeerCoding === undefined ? false : !organization.enableSeerCoding;
@@ -245,6 +251,7 @@ export function AutofixSteps({data, groupId, runId}: AutofixStepsProps) {
245251
step.type === AutofixStepType.CHANGES && !isInitialMount
246252
}
247253
isAutoTriggeredRun={isAutoTriggeredRun}
254+
event={event}
248255
/>
249256
</div>
250257
);

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import {
66
AutofixStepType,
77
type AutofixCodebaseChange,
88
type AutofixData,
9+
type AutofixRootCauseData,
10+
type AutofixSolutionTimelineEvent,
911
} from 'sentry/components/events/autofix/types';
1012
import {t} from 'sentry/locale';
13+
import type {Event} from 'sentry/types/event';
1114
import type {Group} from 'sentry/types/group';
15+
import {formatEventToMarkdown} from 'sentry/views/issueDetails/streamline/hooks/useCopyIssueDetails';
1216

1317
export function getRootCauseDescription(autofixData: AutofixData) {
1418
const rootCause = autofixData.steps?.find(
@@ -59,6 +63,45 @@ export function getSolutionCopyText(autofixData: AutofixData) {
5963
return formatSolutionText(solution.solution, solution.custom_solution);
6064
}
6165

66+
export function formatRootCauseWithEvent(
67+
cause: AutofixRootCauseData | undefined,
68+
customRootCause: string | undefined,
69+
event: Event | undefined
70+
): string {
71+
const rootCauseText = formatRootCauseText(cause, customRootCause);
72+
73+
if (!event) {
74+
return rootCauseText;
75+
}
76+
77+
const eventText = '\n# Raw Event Data\n' + formatEventToMarkdown(event);
78+
return rootCauseText + eventText;
79+
}
80+
81+
export function formatSolutionWithEvent(
82+
solution: AutofixSolutionTimelineEvent[] | undefined,
83+
customSolution: string | undefined,
84+
event: Event | undefined,
85+
rootCause?: AutofixRootCauseData
86+
): string {
87+
let combinedText = '';
88+
89+
if (rootCause) {
90+
const rootCauseText = formatRootCauseText(rootCause);
91+
combinedText += rootCauseText + '\n\n';
92+
}
93+
94+
const solutionText = formatSolutionText(solution || [], customSolution);
95+
combinedText += solutionText;
96+
97+
if (event) {
98+
const eventText = '\n# Raw Event Data\n' + formatEventToMarkdown(event);
99+
combinedText += eventText;
100+
}
101+
102+
return combinedText;
103+
}
104+
62105
export function getSolutionIsLoading(autofixData: AutofixData) {
63106
const solutionProgressStep = autofixData.steps?.find(
64107
step => step.key === 'solution_processing'

0 commit comments

Comments
 (0)