Skip to content

Commit bfeb59e

Browse files
authored
feat(autofix): Add autofix automation tuning to project setting (#90801)
1 parent af27540 commit bfeb59e

File tree

7 files changed

+75
-0
lines changed

7 files changed

+75
-0
lines changed

src/sentry/api/endpoints/project_details.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class ProjectMemberSerializer(serializers.Serializer):
134134
"performanceIssueSendToPlatform",
135135
"tempestFetchScreenshots",
136136
"tempestFetchDumps",
137+
"autofixAutomationTuning",
137138
]
138139
)
139140
class ProjectAdminSerializer(ProjectMemberSerializer):
@@ -228,6 +229,9 @@ class ProjectAdminSerializer(ProjectMemberSerializer):
228229
performanceIssueSendToPlatform = serializers.BooleanField(required=False)
229230
tempestFetchScreenshots = serializers.BooleanField(required=False)
230231
tempestFetchDumps = serializers.BooleanField(required=False)
232+
autofixAutomationTuning = serializers.ChoiceField(
233+
choices=["off", "low", "medium", "high"], required=False
234+
)
231235

232236
# DO NOT ADD MORE TO OPTIONS
233237
# Each param should be a field in the serializer like above.
@@ -455,6 +459,17 @@ def validate_tempestFetchDumps(self, value):
455459
)
456460
return value
457461

462+
def validate_autofixAutomationTuning(self, value):
463+
organization = self.context["project"].organization
464+
actor = self.context["request"].user
465+
if not features.has(
466+
"organizations:trigger-autofix-on-issue-summary", organization, actor=actor
467+
):
468+
raise serializers.ValidationError(
469+
"Organization does not have the trigger-autofix-on-issue-summary feature enabled."
470+
)
471+
return value
472+
458473

459474
class RelaxedProjectPermission(ProjectPermission):
460475
scope_map = {
@@ -762,6 +777,14 @@ def put(self, request: Request, project) -> Response:
762777
"dynamicSamplingBiases"
763778
]
764779

780+
if result.get("autofixAutomationTuning") is not None:
781+
if project.update_option(
782+
"sentry:autofix_automation_tuning", result["autofixAutomationTuning"]
783+
):
784+
changed_proj_settings["sentry:autofix_automation_tuning"] = result[
785+
"autofixAutomationTuning"
786+
]
787+
765788
if has_elevated_scopes:
766789
options = result.get("options", {})
767790
if "sentry:origins" in options:

src/sentry/api/serializers/models/project.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,7 @@ class DetailedProjectResponse(ProjectWithTeamResponseDict):
951951
isDynamicallySampled: bool
952952
tempestFetchScreenshots: NotRequired[bool]
953953
tempestFetchDumps: NotRequired[bool]
954+
autofixAutomationTuning: NotRequired[str]
954955

955956

956957
class DetailedProjectSerializer(ProjectWithTeamSerializer):
@@ -1099,6 +1100,9 @@ def serialize(
10991100
},
11001101
"symbolSources": serialized_sources,
11011102
"isDynamicallySampled": sample_rate is not None and sample_rate < 1.0,
1103+
"autofixAutomationTuning": self.get_value_with_default(
1104+
attrs, "sentry:autofix_automation_tuning"
1105+
),
11021106
}
11031107

11041108
if has_tempest_access(obj.organization, user):

src/sentry/apidocs/examples/project_examples.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@
262262
"tempestFetchScreenshots": False,
263263
"tempestFetchDumps": False,
264264
"isDynamicallySampled": True,
265+
"autofixAutomationTuning": "off",
265266
"highlightTags": [],
266267
"highlightContext": {},
267268
"highlightPreset": {"tags": [], "context": {}},

src/sentry/models/options/project_option.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"sentry:breakdowns",
6666
"sentry:transaction_name_cluster_rules",
6767
"sentry:uptime_autodetection",
68+
"sentry:autofix_automation_tuning",
6869
"quotas:spike-protection-disabled",
6970
"feedback:branding",
7071
"digests:mail:minimum_delay",

src/sentry/projectoptions/defaults.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,6 @@
196196

197197
# Should tempest fetch dumps for this project
198198
register(key="sentry:tempest_fetch_dumps", default=False)
199+
200+
# Should autofix run automatically on new issues
201+
register(key="sentry:autofix_automation_tuning", default="off")

tests/sentry/api/endpoints/test_project_details.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,3 +1941,36 @@ def test_get_tempest_fetch_dumps_options_without_feature_flag(self):
19411941
self.organization.slug, self.project.slug, method="get"
19421942
)
19431943
assert "tempestFetchDumps" not in response.data
1944+
1945+
def test_autofix_automation_tuning(self):
1946+
# Test without feature flag - should fail
1947+
resp = self.get_error_response(
1948+
self.org_slug, self.proj_slug, autofixAutomationTuning="low", status_code=400
1949+
)
1950+
assert (
1951+
"trigger-autofix-on-issue-summary feature enabled"
1952+
in resp.data["autofixAutomationTuning"][0]
1953+
)
1954+
assert self.project.get_option("sentry:autofix_automation_tuning") == "off" # default
1955+
1956+
# Test with feature flag but invalid value - should fail
1957+
with self.feature("organizations:trigger-autofix-on-issue-summary"):
1958+
resp = self.get_error_response(
1959+
self.org_slug, self.proj_slug, autofixAutomationTuning="invalid", status_code=400
1960+
)
1961+
assert '"invalid" is not a valid choice.' in resp.data["autofixAutomationTuning"][0]
1962+
assert self.project.get_option("sentry:autofix_automation_tuning") == "off" # default
1963+
1964+
# Test with feature flag and valid value - should succeed
1965+
resp = self.get_success_response(
1966+
self.org_slug, self.proj_slug, autofixAutomationTuning="medium"
1967+
)
1968+
assert self.project.get_option("sentry:autofix_automation_tuning") == "medium"
1969+
assert resp.data["autofixAutomationTuning"] == "medium"
1970+
1971+
# Test setting back to off
1972+
resp = self.get_success_response(
1973+
self.org_slug, self.proj_slug, autofixAutomationTuning="off"
1974+
)
1975+
assert self.project.get_option("sentry:autofix_automation_tuning") == "off"
1976+
assert resp.data["autofixAutomationTuning"] == "off"

tests/sentry/api/serializers/test_project.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,16 @@ def test_toolbar_allowed_origins(self):
808808
result = serialize(self.project, self.user, DetailedProjectSerializer())
809809
assert result["options"]["sentry:toolbar_allowed_origins"].split("\n") == origins
810810

811+
def test_autofix_automation_tuning_flag(self):
812+
# Default is "off"
813+
result = serialize(self.project, self.user, DetailedProjectSerializer())
814+
assert result["autofixAutomationTuning"] == "off"
815+
816+
# Update the value
817+
self.project.update_option("sentry:autofix_automation_tuning", "high")
818+
result = serialize(self.project, self.user, DetailedProjectSerializer())
819+
assert result["autofixAutomationTuning"] == "high"
820+
811821

812822
class BulkFetchProjectLatestReleases(TestCase):
813823
@cached_property

0 commit comments

Comments
 (0)