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 bc80cf84330..a892b4a913a 100644
--- a/tests/webapp/api/test_performance_alertsummary_api.py
+++ b/tests/webapp/api/test_performance_alertsummary_api.py
@@ -94,6 +94,7 @@ def test_alert_summaries_get(
"push_timestamp",
"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()) == {
@@ -174,6 +175,7 @@ def test_alert_summaries_get_onhold(
"push_timestamp",
"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 7101e78798a..d5a86b35705 100644
--- a/treeherder/webapp/api/performance_serializers.py
+++ b/treeherder/webapp/api/performance_serializers.py
@@ -293,6 +293,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)
@@ -310,6 +311,13 @@ 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)
+ .exclude(id=performance_alert_summary.id)
+ .values_list("id", flat=True)
+ )
+
class Meta:
model = PerformanceAlertSummary
fields = [
@@ -336,6 +344,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 23b418f2c3d..10ffb2e3eac 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,
@@ -240,6 +241,24 @@ const AlertHeader = ({
/>
+ {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 && (