diff --git a/tests/ui/mock/alert_summaries.json b/tests/ui/mock/alert_summaries.json
index d559855bd35..61ef457c60e 100644
--- a/tests/ui/mock/alert_summaries.json
+++ b/tests/ui/mock/alert_summaries.json
@@ -136,6 +136,7 @@
}
],
"related_alerts": [],
+ "duplicated_summaries_ids": [],
"status": 0,
"bug_number": null,
"bug_updated": null,
@@ -223,6 +224,7 @@
}
],
"related_alerts": [],
+ "duplicated_summaries_ids": [],
"status": 0,
"bug_number": null,
"bug_updated": null,
@@ -358,6 +360,7 @@
}
],
"related_alerts": [],
+ "duplicated_summaries_ids": [],
"status": 0,
"bug_number": null,
"bug_updated": null,
diff --git a/tests/webapp/api/test_performance_alertsummary_api.py b/tests/webapp/api/test_performance_alertsummary_api.py
index 82a571e078b..1e5711fb8c8 100644
--- a/tests/webapp/api/test_performance_alertsummary_api.py
+++ b/tests/webapp/api/test_performance_alertsummary_api.py
@@ -95,6 +95,7 @@ def test_alert_summaries_get(
"prev_push_revision",
"original_prev_push_revision",
"performance_tags",
+ "duplicated_summaries_ids",
}
assert len(resp.json()["results"][0]["alerts"]) == 1
assert set(resp.json()["results"][0]["alerts"][0].keys()) == {
@@ -176,6 +177,7 @@ def test_alert_summaries_get_onhold(
"prev_push_revision",
"original_prev_push_revision",
"performance_tags",
+ "duplicated_summaries_ids",
}
assert len(resp.json()["results"][0]["alerts"]) == 1
assert set(resp.json()["results"][0]["alerts"][0].keys()) == {
diff --git a/treeherder/webapp/api/performance_serializers.py b/treeherder/webapp/api/performance_serializers.py
index 9e68e467f82..e34cb83e1e1 100644
--- a/treeherder/webapp/api/performance_serializers.py
+++ b/treeherder/webapp/api/performance_serializers.py
@@ -300,6 +300,7 @@ class PerformanceAlertSummarySerializer(serializers.ModelSerializer):
queryset=User.objects.all(),
)
assignee_email = serializers.SerializerMethodField()
+ duplicated_summaries_ids = serializers.SerializerMethodField()
# marking these fields as readonly, the user should not be modifying them
# (after the item is first created, where we don't use this serializer
# class)
@@ -317,6 +318,15 @@ def update(self, instance, validated_data):
def get_assignee_email(self, performance_alert_summary):
return getattr(performance_alert_summary.assignee, "email", None)
+ def get_duplicated_summaries_ids(self, performance_alert_summary):
+ return (
+ PerformanceAlertSummary.objects.filter(
+ push=performance_alert_summary.push, framework=performance_alert_summary.framework
+ )
+ .exclude(id=performance_alert_summary.id)
+ .values_list("id", flat=True)
+ )
+
class Meta:
model = PerformanceAlertSummary
fields = [
@@ -344,6 +354,7 @@ class Meta:
"assignee_username",
"assignee_email",
"performance_tags",
+ "duplicated_summaries_ids",
]
diff --git a/ui/perfherder/alerts/AlertHeader.jsx b/ui/perfherder/alerts/AlertHeader.jsx
index 39b1240739d..3d722dab894 100644
--- a/ui/perfherder/alerts/AlertHeader.jsx
+++ b/ui/perfherder/alerts/AlertHeader.jsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
+import { Link } from 'react-router-dom';
import {
UncontrolledDropdown,
DropdownMenu,
@@ -230,6 +231,24 @@ const AlertHeader = ({
Revisions have been modified.
)}
+ {alertSummary.duplicated_summaries_ids.length > 0 && (
+
+ Duplicated summaries:
+ {alertSummary.duplicated_summaries_ids.map((id, index) => (
+
+ Alert #{id}
+ {alertSummary.duplicated_summaries_ids.length - 1 !== index &&
+ ', '}
+
+ ))}
+
+ )}
{performanceTags.length > 0 && (