Skip to content

Commit b739ef5

Browse files
committed
Merge branch 'master' into jferg/learn-more
2 parents c198374 + b4db321 commit b739ef5

File tree

290 files changed

+4670
-2104
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

290 files changed

+4670
-2104
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ tests/sentry/api/endpoints/test_organization_dashboard_widget_details.py @ge
523523
/static/app/views/settings/dynamicSampling/ @getsentry/telemetry-experience
524524
/static/app/views/onboarding* @getsentry/telemetry-experience
525525
/static/app/views/projectInstall/ @getsentry/telemetry-experience
526+
/static/app/views/insights/agentMonitoring/ @getsentry/telemetry-experience
527+
/static/app/views/insights/pages/platform/ @getsentry/telemetry-experience
526528
## End of Telemetry Experience
527529

528530

.pre-commit-config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ repos:
143143
|static/app/utils/profiling/profile/formats/typescript/trace\.json
144144
)$
145145
146+
- id: knip
147+
name: knip
148+
language: system
149+
files: \.(ts|js|tsx|jsx|mjs)$
150+
pass_filenames: false
151+
stages: [pre-push]
152+
entry: bash -c 'if [ -n "${SENTRY_KNIP_PRE_PUSH:-}" ]; then exec ./node_modules/.bin/knip; fi' --
153+
146154
- repo: https://github.com/pre-commit/pygrep-hooks
147155
rev: v1.10.0
148156
hooks:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ test-cli: create-db
100100
cd test_cli && sentry init test_conf
101101
cd test_cli && sentry --config=test_conf help
102102
cd test_cli && sentry --config=test_conf upgrade --traceback --noinput
103-
cd test_cli && sentry --config=test_conf export
103+
cd test_cli && sentry --config=test_conf export --help
104104
rm -r test_cli
105105
@echo ""
106106

knip.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const storyBookEntryPoints = [
3333
// our storybook implementation is here
3434
'static/app/stories/storyBook.tsx',
3535
// custom webpack loaders for stories
36-
'static/app/**/stories/*loader.ts',
36+
'static/app/stories/*loader.ts',
3737
];
3838

3939
const config: KnipConfig = {

migrations_lockfile.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ nodestore: 0001_squashed_0002_nodestore_no_dictfield
2121

2222
replays: 0001_squashed_0005_drop_replay_index
2323

24-
sentry: 0906_django_arrayfield_users
24+
sentry: 0909_django_array_field_not_release
2525

2626
social_auth: 0001_squashed_0002_default_auto_field
2727

2828
tempest: 0001_squashed_0002_make_message_type_nullable
2929

3030
uptime: 0001_squashed_0042_extra_uptime_indexes
3131

32-
workflow_engine: 0066_workflow_action_group_status_table
32+
workflow_engine: 0001_squashed_0065_add_status_to_detector_and_workflow

requirements-base.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ botocore>=1.34.8
66
brotli>=1.1.0
77
cachetools>=5
88
celery>=5
9-
click>=8.1
9+
click>=8.2
1010
confluent-kafka>=2.3.0
1111
cronsim>=2.6
1212
cssselect>=1.0.3

requirements-dev-frozen.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ cffi==1.17.1
2525
cfgv==3.3.1
2626
chardet==5.2.0
2727
charset-normalizer==3.4.0
28-
click==8.1.7
29-
click-didyoumean==0.3.0
28+
click==8.2.1
29+
click-didyoumean==0.3.1
3030
click-plugins==1.1.1
3131
click-repl==0.3.0
3232
confluent-kafka==2.8.0

requirements-frozen.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ certifi==2024.7.4
2222
cffi==1.17.1
2323
chardet==5.2.0
2424
charset-normalizer==3.4.0
25-
click==8.1.7
26-
click-didyoumean==0.3.0
25+
click==8.2.1
26+
click-didyoumean==0.3.1
2727
click-plugins==1.1.1
2828
click-repl==0.3.0
2929
confluent-kafka==2.8.0

rspack.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,8 @@ const appConfig: Configuration = {
380380
resolveLoader: {
381381
alias: {
382382
'type-loader': STORYBOOK_TYPES
383-
? path.resolve(__dirname, 'static/app/views/stories/type-loader.ts')
384-
: path.resolve(__dirname, 'static/app/views/stories/noop-type-loader.ts'),
383+
? path.resolve(__dirname, 'static/app/stories/type-loader.ts')
384+
: path.resolve(__dirname, 'static/app/stories/noop-type-loader.ts'),
385385
},
386386
},
387387

src/sentry/api/bases/organization.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ def is_not_2fa_compliant(
6464
if request.user.is_authenticated and request.user.is_sentry_app:
6565
return False
6666

67+
if request.user.is_anonymous:
68+
return False
69+
6770
if is_active_superuser(request):
6871
return False
6972

src/sentry/api/endpoints/group_ai_summary.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from sentry.api.api_publish_status import ApiPublishStatus
1111
from sentry.api.base import region_silo_endpoint
1212
from sentry.api.bases.group import GroupAiEndpoint
13+
from sentry.autofix.utils import SeerAutomationSource
1314
from sentry.models.group import Group
1415
from sentry.seer.issue_summary import get_issue_summary
1516
from sentry.types.ratelimit import RateLimit, RateLimitCategory
@@ -37,7 +38,10 @@ def post(self, request: Request, group: Group) -> Response:
3738
force_event_id = data.get("event_id", None)
3839

3940
summary_data, status_code = get_issue_summary(
40-
group=group, user=request.user, force_event_id=force_event_id, source="issue_details"
41+
group=group,
42+
user=request.user,
43+
force_event_id=force_event_id,
44+
source=SeerAutomationSource.ISSUE_DETAILS,
4145
)
4246

4347
return Response(summary_data, status=status_code)

src/sentry/api/endpoints/group_autofix_setup_check.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
from django.conf import settings
88
from rest_framework.response import Response
99

10+
from sentry import quotas
1011
from sentry.api.api_owners import ApiOwner
1112
from sentry.api.api_publish_status import ApiPublishStatus
1213
from sentry.api.base import region_silo_endpoint
1314
from sentry.api.bases.group import GroupAiEndpoint
1415
from sentry.autofix.utils import get_autofix_repos_from_project_code_mappings
15-
from sentry.constants import ObjectStatus
16+
from sentry.constants import DataCategory, ObjectStatus
1617
from sentry.integrations.services.integration import integration_service
1718
from sentry.models.group import Group
1819
from sentry.models.organization import Organization
@@ -126,6 +127,11 @@ def get(self, request: Request, group: Group) -> Response:
126127
if not user_acknowledgement: # If the user has acknowledged, the org must have too.
127128
org_acknowledgement = get_seer_org_acknowledgement(org_id=org.id)
128129

130+
# TODO return BOTH trial status and autofix quota
131+
has_autofix_quota: bool = quotas.backend.has_available_reserved_budget(
132+
org_id=org.id, data_category=DataCategory.SEER_AUTOFIX
133+
)
134+
129135
return Response(
130136
{
131137
"integration": {
@@ -137,5 +143,8 @@ def get(self, request: Request, group: Group) -> Response:
137143
"orgHasAcknowledged": org_acknowledgement,
138144
"userHasAcknowledged": user_acknowledgement,
139145
},
146+
"billing": {
147+
"hasAutofixQuota": has_autofix_quota,
148+
},
140149
}
141150
)

src/sentry/api/endpoints/organization_events_meta.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,18 +178,16 @@ def get(self, request: Request, organization) -> Response:
178178
except NoProjects:
179179
return Response({})
180180

181-
# TODO: remove useRpc param
182-
use_eap = (
183-
request.GET.get("useRpc", "0") == "1" or request.GET.get("dataset", None) == "spans"
184-
)
181+
use_eap = request.GET.get("dataset", None) == "spans"
185182
orderby = self.get_orderby(request) or ["timestamp"]
186183

187-
if use_eap:
188-
result = get_eap_span_samples(request, snuba_params, orderby)
189-
dataset = spans_rpc
190-
else:
191-
result = get_span_samples(request, snuba_params, orderby)
192-
dataset = spans_indexed
184+
with handle_query_errors():
185+
if use_eap:
186+
result = get_eap_span_samples(request, snuba_params, orderby)
187+
dataset = spans_rpc
188+
else:
189+
result = get_span_samples(request, snuba_params, orderby)
190+
dataset = spans_indexed
193191

194192
return Response(
195193
self.handle_results_with_meta(

src/sentry/api/endpoints/organization_trace_item_attributes.py

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
TraceItemAttributeNamesRequest,
1111
TraceItemAttributeValuesRequest,
1212
)
13+
from sentry_protos.snuba.v1.request_common_pb2 import PageToken
1314
from sentry_protos.snuba.v1.request_common_pb2 import TraceItemType as ProtoTraceItemType
1415
from sentry_protos.snuba.v1.trace_item_attribute_pb2 import AttributeKey
1516

@@ -20,7 +21,7 @@
2021
from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
2122
from sentry.api.endpoints.organization_spans_fields import BaseSpanFieldValuesAutocompletionExecutor
2223
from sentry.api.event_search import translate_escape_sequences
23-
from sentry.api.paginator import ChainPaginator
24+
from sentry.api.paginator import ChainPaginator, GenericOffsetPaginator
2425
from sentry.api.serializers import serialize
2526
from sentry.api.utils import handle_query_errors
2627
from sentry.models.organization import Organization
@@ -35,6 +36,40 @@
3536
from sentry.snuba.referrer import Referrer
3637
from sentry.tagstore.types import TagValue
3738
from sentry.utils import snuba_rpc
39+
from sentry.utils.cursors import Cursor, CursorResult
40+
41+
42+
class TraceItemAttributesNamesPaginator:
43+
"""
44+
This is a bit of a weird paginator.
45+
46+
The trace item attributes RPC returns a list of attribute names from the
47+
database. But depending on the item type, it is possible that there are some
48+
hard coded attribute names that gets appended to the end of the results.
49+
Because of that, the number of results returned can exceed limit + 1.
50+
51+
To handle this nicely, here we choose to return the full set of results
52+
even if it exceeds limit + 1.
53+
"""
54+
55+
def __init__(self, data_fn):
56+
self.data_fn = data_fn
57+
58+
def get_result(self, limit, cursor=None):
59+
if limit <= 0:
60+
raise ValueError(f"invalid limit for paginator, expected >0, got {limit}")
61+
62+
offset = cursor.offset if cursor is not None else 0
63+
# Request 1 more than limit so we can tell if there is another page
64+
data = self.data_fn(offset=offset, limit=limit + 1)
65+
assert isinstance(data, list)
66+
has_more = len(data) >= limit + 1
67+
68+
return CursorResult(
69+
data,
70+
prev=Cursor(0, max(0, offset - limit), True, offset > 0),
71+
next=Cursor(0, max(0, offset + limit), False, has_more),
72+
)
3873

3974

4075
class OrganizationTraceItemAttributesEndpointBase(OrganizationEventsV2EndpointBase):
@@ -166,32 +201,28 @@ def get(self, request: Request, organization: Organization) -> Response:
166201
else AttributeKey.Type.TYPE_STRING
167202
)
168203

169-
rpc_request = TraceItemAttributeNamesRequest(
170-
meta=meta,
171-
limit=max_attributes,
172-
offset=0,
173-
type=attr_type,
174-
value_substring_match=value_substring_match,
175-
intersecting_attributes_filter=filter,
176-
)
204+
def data_fn(offset: int, limit: int):
205+
rpc_request = TraceItemAttributeNamesRequest(
206+
meta=meta,
207+
limit=limit,
208+
page_token=PageToken(offset=offset),
209+
type=attr_type,
210+
value_substring_match=value_substring_match,
211+
intersecting_attributes_filter=filter,
212+
)
177213

178-
with handle_query_errors():
179-
rpc_response = snuba_rpc.attribute_names_rpc(rpc_request)
180-
181-
paginator = ChainPaginator(
182-
[
183-
[
184-
as_attribute_key(attribute.name, serialized["attribute_type"], trace_item_type)
185-
for attribute in rpc_response.attributes
186-
if attribute.name and can_expose_attribute(attribute.name, trace_item_type)
187-
],
188-
],
189-
max_limit=max_attributes,
190-
)
214+
with handle_query_errors():
215+
rpc_response = snuba_rpc.attribute_names_rpc(rpc_request)
216+
217+
return [
218+
as_attribute_key(attribute.name, serialized["attribute_type"], trace_item_type)
219+
for attribute in rpc_response.attributes
220+
if attribute.name and can_expose_attribute(attribute.name, trace_item_type)
221+
]
191222

192223
return self.paginate(
193224
request=request,
194-
paginator=paginator,
225+
paginator=TraceItemAttributesNamesPaginator(data_fn=data_fn),
195226
on_results=lambda results: serialize(results, request.user),
196227
default_per_page=max_attributes,
197228
max_per_page=max_attributes,
@@ -230,24 +261,25 @@ def get(self, request: Request, organization: Organization, key: str) -> Respons
230261
else OURLOG_DEFINITIONS
231262
)
232263

233-
executor = TraceItemAttributeValuesAutocompletionExecutor(
234-
organization=organization,
235-
snuba_params=snuba_params,
236-
key=key,
237-
query=substring_match,
238-
max_span_tag_values=max_attribute_values,
239-
definitions=definitions,
240-
)
241-
242-
with handle_query_errors():
243-
tag_values = executor.execute()
244-
tag_values.sort(key=lambda tag: tag.value)
264+
def data_fn(offset: int, limit: int):
265+
executor = TraceItemAttributeValuesAutocompletionExecutor(
266+
organization=organization,
267+
snuba_params=snuba_params,
268+
key=key,
269+
query=substring_match,
270+
limit=limit,
271+
offset=offset,
272+
definitions=definitions,
273+
)
245274

246-
paginator = ChainPaginator([tag_values], max_limit=max_attribute_values)
275+
with handle_query_errors():
276+
tag_values = executor.execute()
277+
tag_values.sort(key=lambda tag: tag.value)
278+
return tag_values
247279

248280
return self.paginate(
249281
request=request,
250-
paginator=paginator,
282+
paginator=GenericOffsetPaginator(data_fn=data_fn),
251283
on_results=lambda results: serialize(results, request.user),
252284
default_per_page=max_attribute_values,
253285
max_per_page=max_attribute_values,
@@ -261,10 +293,13 @@ def __init__(
261293
snuba_params: SnubaParams,
262294
key: str,
263295
query: str | None,
264-
max_span_tag_values: int,
296+
limit: int,
297+
offset: int,
265298
definitions: ColumnDefinitions,
266299
):
267-
super().__init__(organization, snuba_params, key, query, max_span_tag_values)
300+
super().__init__(organization, snuba_params, key, query, limit)
301+
self.limit = limit
302+
self.offset = offset
268303
self.resolver = SearchResolver(
269304
params=snuba_params, config=SearchResolverConfig(), definitions=definitions
270305
)
@@ -326,7 +361,8 @@ def string_autocomplete_function(self) -> list[TagValue]:
326361
meta=meta,
327362
key=self.attribute_key,
328363
value_substring_match=query,
329-
limit=self.max_span_tag_values,
364+
limit=self.limit,
365+
page_token=PageToken(offset=self.offset),
330366
)
331367
rpc_response = snuba_rpc.attribute_values_rpc(rpc_request)
332368

0 commit comments

Comments
 (0)