Skip to content

Commit

Permalink
Merge branch 'master' into DominikB2014/add-view-to-analytic-event-name
Browse files Browse the repository at this point in the history
  • Loading branch information
DominikB2014 authored Nov 7, 2024
2 parents 73eb697 + d88cbbb commit 2d43132
Show file tree
Hide file tree
Showing 13 changed files with 428 additions and 1 deletion.
22 changes: 22 additions & 0 deletions src/sentry/api/endpoints/organization_events_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,28 @@ class OrganizationEventsTraceMetaEndpoint(OrganizationEventsV2EndpointBase):
}
snuba_methods = ["GET"]

def get_projects(
self,
request: HttpRequest,
organization: Organization | RpcOrganization,
force_global_perms: bool = False,
include_all_accessible: bool = False,
project_ids: set[int] | None = None,
project_slugs: set[str] | None = None,
) -> list[Project]:
"""The trace endpoint always wants to get all projects regardless of what's passed into the API
This is because a trace can span any number of projects in an organization. But we still want to
use the get_projects function to check for any permissions. So we'll just pass project_ids=-1 everytime
which is what would be sent if we wanted all projects"""
return super().get_projects(
request,
organization,
project_ids={-1},
project_slugs=None,
include_all_accessible=True,
)

def get(self, request: Request, organization: Organization, trace_id: str) -> HttpResponse:
if not self.has_feature(organization, request):
return Response(status=404)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {CloudResourceContext} from '@sentry/types';
import {EventFixture} from 'sentry-fixture/event';

import {render, screen} from 'sentry-test/reactTestingLibrary';

import type {CloudResourceContext} from 'sentry/components/events/contexts/cloudResource';
import {getCloudResourceContextData} from 'sentry/components/events/contexts/cloudResource';
import ContextCard from 'sentry/components/events/contexts/contextCard';

Expand Down
84 changes: 84 additions & 0 deletions static/app/components/events/contexts/culture.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {EventFixture} from 'sentry-fixture/event';

import {render, screen} from 'sentry-test/reactTestingLibrary';

import ContextCard from 'sentry/components/events/contexts/contextCard';
import {
type CultureContext,
getCultureContextData,
} from 'sentry/components/events/contexts/culture';

const MOCK_CULTURE_CONTEXT: CultureContext = {
calendar: 'GregorianCalendar',
display_name: 'English (United States)',
locale: 'en-US',
is_24_hour_format: true,
timezone: 'Europe/Vienna',
// Extra data is still valid and preserved
extra_data: 'something',
unknown_key: 123,
};

const MOCK_REDACTION = {
['timezone']: {
'': {
chunks: [
{
remark: 'x',
rule_id: 'project:0',
text: '',
type: 'redaction',
},
],
len: 13,
rem: [['project:0', 'x', 0, 0]],
},
},
};

describe('CultureContext', function () {
it('returns formatted data correctly', function () {
expect(getCultureContextData({data: MOCK_CULTURE_CONTEXT})).toEqual([
{key: 'calendar', subject: 'Calendar', value: 'GregorianCalendar'},
{
key: 'display_name',
subject: 'Display Name',
value: 'English (United States)',
},
{key: 'locale', subject: 'Locale', value: 'en-US'},
{key: 'is_24_hour_format', subject: 'Uses 24h Format', value: true},
{key: 'timezone', subject: 'Timezone', value: 'Europe/Vienna'},
{
key: 'extra_data',
subject: 'extra_data',
value: 'something',
},
{
key: 'unknown_key',
subject: 'unknown_key',
value: 123,
},
]);
});

it('renders with meta annotations correctly', function () {
const event = EventFixture({
_meta: {contexts: {culture: MOCK_REDACTION}},
});

render(
<ContextCard
event={event}
type={'culture'}
alias={'culture'}
value={{...MOCK_CULTURE_CONTEXT, timezone: ''}}
/>
);

expect(screen.getByText('Culture')).toBeInTheDocument();
expect(screen.getByText('Uses 24h Format')).toBeInTheDocument();
expect(screen.getByText('true')).toBeInTheDocument();
expect(screen.getByText('Timezone')).toBeInTheDocument();
expect(screen.getByText(/redacted/)).toBeInTheDocument();
});
});
71 changes: 71 additions & 0 deletions static/app/components/events/contexts/culture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {t} from 'sentry/locale';
import type {KeyValueListData} from 'sentry/types/group';

// https://develop.sentry.dev/sdk/data-model/event-payloads/contexts/#culture-context
const enum CultureContextKeys {
CALENDAR = 'calendar',
DISPLAY_NAME = 'display_name',
LOCALE = 'locale',
IS_24_HOUR = 'is_24_hour_format',
TIMEZONE = 'timezone',
}

export interface CultureContext {
// Any custom keys users may set
[key: string]: any;
[CultureContextKeys.CALENDAR]?: string;
[CultureContextKeys.DISPLAY_NAME]?: string;
[CultureContextKeys.LOCALE]?: string;
[CultureContextKeys.IS_24_HOUR]?: boolean;
[CultureContextKeys.TIMEZONE]?: string;
}

export function getCultureContextData({
data = {},
meta,
}: {
data: CultureContext;
meta?: Record<keyof CultureContext, any>;
}): KeyValueListData {
return Object.keys(data).map(ctxKey => {
switch (ctxKey) {
case CultureContextKeys.CALENDAR:
return {
key: ctxKey,
subject: t('Calendar'),
value: data[CultureContextKeys.CALENDAR],
};
case CultureContextKeys.DISPLAY_NAME:
return {
key: ctxKey,
subject: t('Display Name'),
value: data[CultureContextKeys.DISPLAY_NAME],
};
case CultureContextKeys.LOCALE:
return {
key: ctxKey,
subject: t('Locale'),
value: data[CultureContextKeys.LOCALE],
};
case CultureContextKeys.IS_24_HOUR:
return {
key: ctxKey,
subject: t('Uses 24h Format'),
value: data[CultureContextKeys.IS_24_HOUR],
};
case CultureContextKeys.TIMEZONE:
return {
key: ctxKey,
subject: t('Timezone'),
value: data[CultureContextKeys.TIMEZONE],
};
default:
return {
key: ctxKey,
subject: ctxKey,
value: data[ctxKey],
meta: meta?.[ctxKey]?.[''],
};
}
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {EventFixture} from 'sentry-fixture/event';

import {render, screen} from 'sentry-test/reactTestingLibrary';

import ContextCard from 'sentry/components/events/contexts/contextCard';
import {
getMissingInstrumentationContextData,
type MissingInstrumentationContext,
} from 'sentry/components/events/contexts/missingInstrumentation';

const MOCK_MISSING_INSTRUMENTATION_CONTEXT: MissingInstrumentationContext = {
package: 'express',
'javascript.is_cjs': true,
// Extra data is still valid and preserved
extra_data: 'something',
unknown_key: 123,
};

const MOCK_REDACTION = {
['package']: {
'': {
chunks: [
{
remark: 'x',
rule_id: 'project:0',
text: '',
type: 'redaction',
},
],
len: 7,
rem: [['project:0', 'x', 0, 0]],
},
},
};

describe('MissingInstrumentationContext', function () {
it('returns formatted data correctly', function () {
expect(
getMissingInstrumentationContextData({data: MOCK_MISSING_INSTRUMENTATION_CONTEXT})
).toEqual([
{
key: 'package',
subject: 'Package w/o Instrumentation',
value: 'express',
},
{
key: 'javascript.is_cjs',
subject: 'From CommonJS Module?',
value: true,
},
{
key: 'extra_data',
subject: 'extra_data',
value: 'something',
meta: undefined,
},
{
key: 'unknown_key',
subject: 'unknown_key',
value: 123,
meta: undefined,
},
]);
});

it('renders with meta annotations correctly', function () {
const event = EventFixture({
_meta: {contexts: {missing_instrumentation: MOCK_REDACTION}},
});

render(
<ContextCard
event={event}
type={'missing_instrumentation'}
alias={'missing_instrumentation'}
value={{...MOCK_MISSING_INSTRUMENTATION_CONTEXT, package: ''}}
/>
);

expect(screen.getByText('Missing OTEL Instrumentation')).toBeInTheDocument();
expect(screen.getByText('From CommonJS Module?')).toBeInTheDocument();
expect(screen.getByText('true')).toBeInTheDocument();
expect(screen.getByText('Package w/o Instrumentation')).toBeInTheDocument();
expect(screen.getByText(/redacted/)).toBeInTheDocument();
});
});
47 changes: 47 additions & 0 deletions static/app/components/events/contexts/missingInstrumentation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {t} from 'sentry/locale';
import type {KeyValueListData} from 'sentry/types/group';

// https://develop.sentry.dev/sdk/data-model/event-payloads/contexts/#missing-instrumentation-context
const enum MissingInstrumentationContextKeys {
PACKAGE = 'package',
FROM_COMMONJS = 'javascript.is_cjs',
}

export interface MissingInstrumentationContext {
// Any custom keys users may set
[key: string]: any;
[MissingInstrumentationContextKeys.PACKAGE]?: string;
[MissingInstrumentationContextKeys.FROM_COMMONJS]?: boolean;
}

export function getMissingInstrumentationContextData({
data = {},
meta,
}: {
data: MissingInstrumentationContext;
meta?: Record<keyof MissingInstrumentationContext, any>;
}): KeyValueListData {
return Object.keys(data).map(ctxKey => {
switch (ctxKey) {
case MissingInstrumentationContextKeys.PACKAGE:
return {
key: ctxKey,
subject: t('Package w/o Instrumentation'),
value: data[MissingInstrumentationContextKeys.PACKAGE],
};
case MissingInstrumentationContextKeys.FROM_COMMONJS:
return {
key: ctxKey,
subject: t('From CommonJS Module?'),
value: data[MissingInstrumentationContextKeys.FROM_COMMONJS],
};
default:
return {
key: ctxKey,
subject: ctxKey,
value: data[ctxKey],
meta: meta?.[ctxKey]?.[''],
};
}
});
}
12 changes: 12 additions & 0 deletions static/app/components/events/contexts/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
type ContextIconProps,
getLogoImage,
} from 'sentry/components/events/contexts/contextIcon';
import {getCultureContextData} from 'sentry/components/events/contexts/culture';
import {getMissingInstrumentationContextData} from 'sentry/components/events/contexts/missingInstrumentation';
import {userContextToActor} from 'sentry/components/events/interfaces/utils';
import StructuredEventData from 'sentry/components/structuredEventData';
import {t} from 'sentry/locale';
Expand Down Expand Up @@ -347,6 +349,11 @@ export function getContextTitle({
return 'OpenTelemetry';
case 'cloud_resource':
return t('Cloud Resource');
case 'culture':
case 'Current Culture':
return t('Culture');
case 'missing_instrumentation':
return t('Missing OTEL Instrumentation');
case 'unity':
return 'Unity';
case 'memory_info': // Current value for memory info
Expand Down Expand Up @@ -547,6 +554,11 @@ export function getFormattedContextData({
];
case 'cloud_resource':
return getCloudResourceContextData({data: contextValue, meta});
case 'culture':
case 'Current Culture':
return getCultureContextData({data: contextValue, meta});
case 'missing_instrumentation':
return getMissingInstrumentationContextData({data: contextValue, meta});
default:
return getDefaultContextData(contextValue);
}
Expand Down
5 changes: 5 additions & 0 deletions static/app/types/event.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {CloudResourceContext} from '@sentry/types';

import type {CultureContext} from 'sentry/components/events/contexts/culture';
import type {MissingInstrumentationContext} from 'sentry/components/events/contexts/missingInstrumentation';
import type {
AggregateSpanType,
MetricsSummary,
Expand Down Expand Up @@ -644,15 +646,18 @@ export type FeatureFlag = {flag: string; result: boolean};
export type Flags = {values: FeatureFlag[]};

export type EventContexts = {
'Current Culture'?: CultureContext;
'Memory Info'?: MemoryInfoContext;
'ThreadPool Info'?: ThreadPoolInfoContext;
browser?: BrowserContext;
client_os?: OSContext;
cloud_resource?: CloudResourceContext;
culture?: CultureContext;
device?: DeviceContext;
feedback?: Record<string, any>;
flags?: Flags;
memory_info?: MemoryInfoContext;
missing_instrumentation?: MissingInstrumentationContext;
os?: OSContext;
otel?: OtelContext;
// TODO (udameli): add better types here
Expand Down
Loading

0 comments on commit 2d43132

Please sign in to comment.