feat(notifications): add test-send endpoint for single-address preview
POST /api/v1/notifications/schedules/<pk>/test-send/ accepts {"email": "..."},
renders the schedule's email, delivers to that address only with [TEST] prefix.
Does not touch last_run_at or last_status.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,7 @@ urlpatterns = [
|
||||
path('notifications/schedules/<int:pk>/recipients/', views.NotificationRecipientView.as_view(), name='notification-recipient-create'),
|
||||
path('notifications/schedules/<int:pk>/recipients/<int:rid>/', views.NotificationRecipientDetailView.as_view(), name='notification-recipient-detail'),
|
||||
path('notifications/schedules/<int:pk>/send-now/', views.NotificationScheduleSendNowView.as_view(), name='notification-schedule-send-now'),
|
||||
path('notifications/schedules/<int:pk>/test-send/', views.NotificationScheduleTestSendView.as_view(), name='notification-schedule-test-send'),
|
||||
|
||||
# Ad Control
|
||||
path('ad-control/', include('ad_control.urls')),
|
||||
|
||||
@@ -2783,3 +2783,51 @@ class NotificationScheduleSendNowView(APIView):
|
||||
'recipientCount': recipient_count,
|
||||
'schedule': _serialize_schedule(schedule),
|
||||
})
|
||||
|
||||
|
||||
class NotificationScheduleTestSendView(APIView):
|
||||
"""Send a preview of this schedule's email to a single address.
|
||||
|
||||
Does NOT update last_run_at / last_status — purely for previewing content.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk):
|
||||
from notifications.models import NotificationSchedule
|
||||
from notifications.emails import BUILDERS
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMessage
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
schedule = get_object_or_404(NotificationSchedule, pk=pk)
|
||||
|
||||
email = (request.data.get('email') or '').strip().lower()
|
||||
if not email:
|
||||
return Response({'error': 'email is required'}, status=400)
|
||||
|
||||
builder = BUILDERS.get(schedule.notification_type)
|
||||
if builder is None:
|
||||
return Response(
|
||||
{'error': f'No builder for type: {schedule.notification_type}'},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
subject, html = builder(schedule)
|
||||
subject = f'[TEST] {subject}'
|
||||
msg = EmailMessage(
|
||||
subject=subject,
|
||||
body=html,
|
||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||
to=[email],
|
||||
)
|
||||
msg.content_subtype = 'html'
|
||||
msg.send(fail_silently=False)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
log('error', f'Test-send failed for schedule #{pk}: {exc}',
|
||||
request=request, user=request.user)
|
||||
return Response({'error': str(exc)}, status=500)
|
||||
|
||||
log('info', f'Test email sent for schedule #{pk} → {email}',
|
||||
request=request, user=request.user)
|
||||
return Response({'ok': True, 'sentTo': email})
|
||||
|
||||
Reference in New Issue
Block a user