- IGmailMailSender (mockable) + MailKit GmailMailSender (smtp.gmail.com:587 STARTTLS, App
Password). SendReplyService builds a threaded reply: In-Reply-To = original Message-Id,
References = stored chain + original (deduped), Subject = 'Re: ' (no double-prefix); sends,
then sets Status=Answered + AnsweredAt + stores sent body in DraftReply. Never auto-sends.
- POST /api/requests/{id}/send {body}: 200 updated RequestDto; 404 unknown; 503 MailNotConfigured;
502 MailAuthFailed (UI hint: re-enter App Password). No entity change (no migration).
- Tests (fake SMTP): threading headers, Answered transition, no-double-Re, not-configured +
auth-failure keep status; BuildMimeMessage header mapping. Live send gated on App Password.
42 lines
1.4 KiB
C#
42 lines
1.4 KiB
C#
using GerbilManagerWebAPI.Models;
|
|
|
|
namespace GerbilManagerWebAPI.Dtos
|
|
{
|
|
public record RequestDto(
|
|
Guid Id,
|
|
string GmailMessageId,
|
|
string? ThreadId,
|
|
string FromAddress,
|
|
string? FromName,
|
|
string? Subject,
|
|
string? BodyText,
|
|
DateTimeOffset ReceivedAt,
|
|
RequestStatus Status,
|
|
Guid? AssignedContactId,
|
|
string? DraftReply,
|
|
DateTimeOffset? AnsweredAt);
|
|
|
|
/// <summary>Triage update: change status and/or assign a contact.</summary>
|
|
public record RequestTriageInput(RequestStatus? Status, Guid? AssignedContactId);
|
|
|
|
/// <summary>Mail settings read shape — the App Password is NEVER returned (only HasAppPassword).</summary>
|
|
public record MailSettingsDto(
|
|
string? GmailAddress,
|
|
int PollIntervalMinutes,
|
|
string Folder,
|
|
bool BackgroundPollEnabled,
|
|
bool HasAppPassword);
|
|
|
|
/// <summary>Body of the (edited) reply to send for POST /api/requests/{id}/send.</summary>
|
|
public record SendReplyInput(string Body);
|
|
|
|
/// <summary>Mail settings write shape. AppPassword is write-only; null/omitted leaves it unchanged,
|
|
/// empty string clears it.</summary>
|
|
public record MailSettingsInput(
|
|
string? GmailAddress,
|
|
string? AppPassword,
|
|
int? PollIntervalMinutes,
|
|
string? Folder,
|
|
bool? BackgroundPollEnabled);
|
|
}
|