diff --git a/libs/shared/shared/django_apps/upload_breadcrumbs/admin.py b/libs/shared/shared/django_apps/upload_breadcrumbs/admin.py
index 00f0310f7..859d9bc00 100644
--- a/libs/shared/shared/django_apps/upload_breadcrumbs/admin.py
+++ b/libs/shared/shared/django_apps/upload_breadcrumbs/admin.py
@@ -560,9 +560,6 @@ def get_urls(self):
@admin.display(description="Actions", ordering=None)
def resend_upload_button(self, obj: UploadBreadcrumb) -> str:
"""Display resend button in the list view for failed uploads."""
- if not self._is_failed_upload(obj):
- return "-"
-
resend_url = reverse(
"admin:upload_breadcrumbs_uploadbreadcrumb_resend_upload", args=[obj.id]
)
@@ -577,24 +574,16 @@ def resend_upload_action(self, obj: UploadBreadcrumb) -> str:
if not obj.pk: # New object
return "-"
- html_parts = []
-
- if self._is_failed_upload(obj):
- resend_url = reverse(
- "admin:upload_breadcrumbs_uploadbreadcrumb_resend_upload", args=[obj.id]
- )
- html_parts.append(
- f'🔄 Resend Upload"
- "
"
- "
⚠️ Note: This will create a new upload task for the same commit and repository. "
- "The original upload data may no longer be available in storage.
"
- )
- else:
- html_parts.append(
- "✅ This upload does not appear to have failed. Resend option is not available.
"
- )
-
+ resend_url = reverse(
+ "admin:upload_breadcrumbs_uploadbreadcrumb_resend_upload", args=[obj.id]
+ )
+ html_parts = [
+ f'🔄 Resend Upload"
+ "
"
+ "⚠️ Note: This will create a new upload task for the same commit and repository. "
+ "The original upload data may no longer be available in storage.
"
+ ]
return format_html("".join(html_parts))
def _is_failed_upload(self, obj: UploadBreadcrumb) -> bool:
@@ -624,10 +613,6 @@ def resend_upload_view(self, request, object_id):
messages.error(request, "Upload breadcrumb not found.")
return redirect("admin:upload_breadcrumbs_uploadbreadcrumb_changelist")
- if not self._is_failed_upload(breadcrumb):
- messages.error(request, "This upload does not appear to have failed.")
- return redirect("admin:upload_breadcrumbs_uploadbreadcrumb_changelist")
-
success, error_message = self._resend_upload(breadcrumb, request.user)
if success:
@@ -706,6 +691,17 @@ def _resend_upload(
"resend_timestamp": timezone.now().isoformat(),
}
+ # Check if upload_ids is None
+ if breadcrumb.upload_ids is None:
+ log.error(
+ "Cannot resend upload - breadcrumb has no upload IDs",
+ extra={"breadcrumb_id": breadcrumb.id},
+ )
+ return (
+ False,
+ "This breadcrumb has no associated upload IDs. It may be a resend breadcrumb that hasn't been processed yet.",
+ )
+
log.info(
f"Collecting upload data for {len(breadcrumb.upload_ids)} uploads",
extra={"upload_ids": breadcrumb.upload_ids},
diff --git a/libs/shared/shared/django_apps/upload_breadcrumbs/tests/test_admin.py b/libs/shared/shared/django_apps/upload_breadcrumbs/tests/test_admin.py
index eb017960f..44ab1afe2 100644
--- a/libs/shared/shared/django_apps/upload_breadcrumbs/tests/test_admin.py
+++ b/libs/shared/shared/django_apps/upload_breadcrumbs/tests/test_admin.py
@@ -1063,8 +1063,8 @@ def test_resend_upload_button_scenarios(self):
{
"milestone": Milestones.UPLOAD_COMPLETE.value,
},
- None,
- str,
+ ["🔄 Resend", 'class="button"', "onclick="],
+ SafeString,
"successful upload",
),
]
@@ -1079,16 +1079,13 @@ def test_resend_upload_button_scenarios(self):
breadcrumb = UploadBreadcrumbFactory(breadcrumb_data=breadcrumb_data)
result = self.admin.resend_upload_button(breadcrumb)
- if expected_contains is None:
- self.assertEqual(result, "-", f"Failed for: {description}")
- else:
- self.assertIsInstance(
- result, expected_type, f"Failed for: {description}"
+ self.assertIsInstance(
+ result, expected_type, f"Failed for: {description}"
+ )
+ for expected_content in expected_contains:
+ self.assertIn(
+ expected_content, result, f"Failed for: {description}"
)
- for expected_content in expected_contains:
- self.assertIn(
- expected_content, result, f"Failed for: {description}"
- )
def test_resend_upload_action_scenarios(self):
"""Test resend_upload_action with various scenarios."""
@@ -1120,10 +1117,7 @@ def test_resend_upload_action_scenarios(self):
},
"abcdef1234567890",
True,
- [
- "✅ This upload does not appear to have failed",
- "Resend option is not available",
- ],
+ ["🔄 Resend Upload", 'class="button default"', "abcdef1", "⚠️ Note:"],
"successful upload",
),
({}, "abcdef1234567890", False, None, "new object without pk"),
@@ -1170,22 +1164,27 @@ def test_resend_upload_view_with_nonexistent_breadcrumb(self, mock_messages_erro
request, "Upload breadcrumb not found."
)
- @patch("django.contrib.messages.error")
- def test_resend_upload_view_with_non_failed_upload(self, mock_messages_error):
- """Test resend_upload_view handles non-failed uploads."""
+ @patch("django.contrib.messages.success")
+ def test_resend_upload_view_with_non_failed_upload(self, mock_messages_success):
+ """Test resend_upload_view allows resending non-failed uploads."""
breadcrumb_data = {
"milestone": Milestones.UPLOAD_COMPLETE.value,
}
- breadcrumb = UploadBreadcrumbFactory(breadcrumb_data=breadcrumb_data)
+ breadcrumb = UploadBreadcrumbFactory(
+ breadcrumb_data=breadcrumb_data, commit_sha="abcdef1234567890"
+ )
request = MagicMock()
request.user = self.user
- with patch.object(self.admin, "get_object", return_value=breadcrumb):
+ with (
+ patch.object(self.admin, "get_object", return_value=breadcrumb),
+ patch.object(self.admin, "_resend_upload", return_value=(True, None)),
+ ):
response = self.admin.resend_upload_view(request, str(breadcrumb.id))
- mock_messages_error.assert_called_once_with(
- request, "This upload does not appear to have failed."
- )
+ mock_messages_success.assert_called_once()
+ success_message = mock_messages_success.call_args[0][1]
+ self.assertIn("Upload resend triggered successfully", success_message)
@patch("django.contrib.messages.success")
def test_resend_upload_view_successful_resend(self, mock_messages_success):