Skip to content

Commit b3d3230

Browse files
authored
feat(contexts): Hide client-related highlights in backend issues (#88539)
If a backend error is thrown in a Meta-Framework, the issue includes `client_os` and `os` in the event context. However, the highlights should only show data relevant to the backend. Also, the runtime is more important than OS, so it's reordered. Also fixes an issue in a test from #86857 as `client_os` is not a context type, but only an alias fixes: getsentry/projects#801 part of: #85732 **Follow-up PR** - #88536 --- ### Before ![image](https://github.com/user-attachments/assets/e972c62b-5255-4287-847a-96890590c10d) or when it ran locally (server is macOS as well) ![image](https://github.com/user-attachments/assets/193c620a-992a-4894-8a4b-789f571b1275) ### After ![image](https://github.com/user-attachments/assets/212b7f3d-35e0-4272-b594-022ab5e64c4c) or locally: ![image](https://github.com/user-attachments/assets/b5485af1-37ce-4f13-9733-5aaf913a6414)
1 parent 0b1c559 commit b3d3230

File tree

4 files changed

+129
-5
lines changed

4 files changed

+129
-5
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import {EventFixture} from 'sentry-fixture/event';
2+
3+
import {getOrderedContextItems} from 'sentry/components/events/contexts';
4+
import type {Event} from 'sentry/types/event';
5+
6+
describe('getOrderedContextItems', function () {
7+
it('orders context items correctly', function () {
8+
const event: Partial<Event> = {
9+
user: {
10+
id: '12345',
11+
12+
},
13+
contexts: {
14+
runtime: {name: 'node', type: 'runtime'},
15+
os: {
16+
name: 'macOS',
17+
version: '15.3.2',
18+
build: '24D81',
19+
kernel_version: '24.3.0',
20+
type: 'os',
21+
},
22+
browser: {name: 'Chrome', version: '134'},
23+
response: {type: 'response', data: {testing: 'lalala'}},
24+
feedback: {rating: 5, comment: 'Great!'},
25+
},
26+
};
27+
28+
const mockEvent = EventFixture(event);
29+
30+
const items = getOrderedContextItems(mockEvent);
31+
32+
expect(items).toHaveLength(6);
33+
34+
const aliasOrder = items.map(item => item.alias);
35+
expect(aliasOrder[0]).toBe('response');
36+
expect(aliasOrder[1]).toBe('feedback');
37+
expect(aliasOrder[2]).toBe('user');
38+
expect(aliasOrder[3]).toBe('runtime');
39+
expect(aliasOrder[4]).toBe('os');
40+
});
41+
42+
it('does not fail with missing context items', function () {
43+
const mockEventOnlyOs = EventFixture({
44+
contexts: {
45+
os: {
46+
os: 'macOS 15.3.2',
47+
name: 'macOS',
48+
version: '15.3.2',
49+
type: 'os',
50+
},
51+
},
52+
});
53+
54+
const itemsOnlyOs = getOrderedContextItems(mockEventOnlyOs);
55+
const osIndex = itemsOnlyOs.findIndex(item => item.alias === 'os');
56+
expect(osIndex).not.toBe(-1);
57+
});
58+
59+
it('filters out empty contexts and contexts only with type', function () {
60+
const mockEvent = EventFixture({
61+
contexts: {
62+
runtime: {
63+
runtime: 'node v20.18.3',
64+
name: 'node',
65+
version: 'v20.18.3',
66+
type: 'runtime',
67+
},
68+
empty: {},
69+
onlyType: {
70+
type: 'default',
71+
},
72+
},
73+
});
74+
75+
const items = getOrderedContextItems(mockEvent);
76+
77+
const emptyIndex = items.findIndex(item => item.alias === 'empty');
78+
const onlyTypeIndex = items.findIndex(item => item.alias === 'onlyType');
79+
80+
expect(emptyIndex).toBe(-1);
81+
expect(onlyTypeIndex).toBe(-1);
82+
});
83+
});

static/app/components/events/contexts/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ export function getOrderedContextItems(event: Event): ContextItem[] {
3636

3737
// hide `flags` in the contexts section since we display this
3838
// info in the feature flag section below
39-
const {feedback, response, flags: _, ...otherContexts} = contexts ?? {};
39+
const {feedback, response, runtime, os, flags: _, ...otherContexts} = contexts ?? {};
4040
const orderedContext: Array<[ContextItem['alias'], ContextValue]> = [
4141
['response', response],
4242
['feedback', feedback],
4343
['user', {...userContext, ...(customUserData as any)}],
44+
['runtime', runtime],
45+
['os', os],
4446
...Object.entries(otherContexts),
4547
];
4648
// For these context aliases, use the alias as 'type' rather than 'value.type'

static/app/components/events/highlights/highlightsIconSummary.spec.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,41 @@ describe('HighlightsIconSummary', function () {
100100
expect(screen.getAllByRole('img')).toHaveLength(4);
101101
});
102102

103+
it('hides client_os and browser contexts for Meta-Framework backend issues', function () {
104+
const duplicateOsContextEvent = EventFixture({
105+
contexts: {
106+
client_os: {
107+
type: 'os',
108+
name: 'macOS',
109+
version: '15.3',
110+
},
111+
os: {
112+
type: 'os',
113+
name: 'Linux',
114+
version: '5.10.243',
115+
},
116+
runtime: {
117+
name: 'node',
118+
runtime: 'node v20.18.3',
119+
type: 'runtime',
120+
version: 'v20.18.3',
121+
},
122+
},
123+
});
124+
render(<HighlightsIconSummary event={duplicateOsContextEvent} group={group} />);
125+
expect(screen.getByText('Linux')).toBeInTheDocument();
126+
expect(screen.getByText('5.10.243')).toBeInTheDocument();
127+
expect(screen.getByText('node')).toBeInTheDocument();
128+
expect(screen.getByText('v20.18.3')).toBeInTheDocument();
129+
expect(screen.queryByText('macOS')).not.toBeInTheDocument();
130+
expect(screen.queryByText('15.3')).not.toBeInTheDocument();
131+
});
132+
103133
it('deduplicates client_os and os contexts', function () {
104134
const duplicateOsContextEvent = EventFixture({
105135
contexts: {
106136
client_os: {
107-
type: 'client_os',
137+
type: 'os',
108138
name: 'macOS',
109139
},
110140
os: {

static/app/components/events/highlights/highlightsIconSummary.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,13 @@ export function HighlightsIconSummary({event, group}: HighlightsIconSummaryProps
5252
// Hide device for non-native platforms since it's mostly duplicate of the client_os or os context
5353
const shouldDisplayDevice =
5454
isMobilePlatform(projectPlatform) || isNativePlatform(projectPlatform);
55-
// For now, highlight icons are only interpretted from context. We should extend this to tags
55+
56+
// Errors thrown on the backend of a Meta-Framework (e.g. Next.js) also include the client context
57+
const isMetaFrameworkBackendIssue =
58+
Object.keys(event.contexts).includes('client_os') &&
59+
Object.keys(event.contexts).includes('os');
60+
61+
// For now, highlight icons are only interpreted from context. We should extend this to tags
5662
// eventually, but for now, it'll match the previous expectations.
5763
const items = getOrderedContextItems(event)
5864
.map(item => ({
@@ -71,8 +77,11 @@ export function HighlightsIconSummary({event, group}: HighlightsIconSummaryProps
7177
}),
7278
}))
7379
.filter((item, _index, array) => {
74-
// Prefer the "os" context for OS information
75-
if (item.contextType === 'client_os' && array.find(i => i.contextType === 'os')) {
80+
if (
81+
// Hide client information in backend issues (always prefer `os` over `client_os`)
82+
isMetaFrameworkBackendIssue &&
83+
(item.contextType === 'browser' || item.alias === 'client_os')
84+
) {
7685
return false;
7786
}
7887

0 commit comments

Comments
 (0)