init push
This commit is contained in:
102
internal/service/ses_event.go
Normal file
102
internal/service/ses_event.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrInvalidSESEvent = errors.New("invalid ses event notification")
|
||||
|
||||
type SESEventNotification struct {
|
||||
EventType string `json:"eventType"`
|
||||
Mail SESEventMail `json:"mail"`
|
||||
Bounce *SESBounceEvent `json:"bounce,omitempty"`
|
||||
Complaint *SESComplaintEvent `json:"complaint,omitempty"`
|
||||
Delivery *SESDeliveryEvent `json:"delivery,omitempty"`
|
||||
Metadata map[string]any `json:"-"`
|
||||
RawPayload map[string]json.RawMessage `json:"-"`
|
||||
}
|
||||
|
||||
type SESEventMail struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
MessageID string `json:"messageId"`
|
||||
Source string `json:"source"`
|
||||
Destination []string `json:"destination"`
|
||||
Tags map[string][]string `json:"tags,omitempty"`
|
||||
ConfigurationSet string `json:"configurationSet,omitempty"`
|
||||
}
|
||||
|
||||
type SESBounceEvent struct {
|
||||
BounceType string `json:"bounceType,omitempty"`
|
||||
BounceSubType string `json:"bounceSubType,omitempty"`
|
||||
ReportingMTA string `json:"reportingMTA,omitempty"`
|
||||
RemoteMTAIP string `json:"remoteMtaIp,omitempty"`
|
||||
FeedbackID string `json:"feedbackId,omitempty"`
|
||||
FeedbackSourceArn string `json:"feedbackSourceArn,omitempty"`
|
||||
}
|
||||
|
||||
type SESComplaintEvent struct {
|
||||
ComplaintFeedbackType string `json:"complaintFeedbackType,omitempty"`
|
||||
UserAgent string `json:"userAgent,omitempty"`
|
||||
FeedbackID string `json:"feedbackId,omitempty"`
|
||||
}
|
||||
|
||||
type SESDeliveryEvent struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ProcessingTimeMillis int64 `json:"processingTimeMillis,omitempty"`
|
||||
ReportingMTA string `json:"reportingMTA,omitempty"`
|
||||
SMTPResponse string `json:"smtpResponse,omitempty"`
|
||||
RemoteMTAIP string `json:"remoteMtaIp,omitempty"`
|
||||
}
|
||||
|
||||
type snsEnvelope struct {
|
||||
Type string `json:"Type"`
|
||||
Message string `json:"Message"`
|
||||
}
|
||||
|
||||
func ParseSESEventNotification(body []byte) (*SESEventNotification, error) {
|
||||
body = []byte(strings.TrimSpace(string(body)))
|
||||
if len(body) == 0 {
|
||||
return nil, ErrInvalidSESEvent
|
||||
}
|
||||
|
||||
if inner, ok := unwrapSNSMessage(body); ok {
|
||||
body = inner
|
||||
}
|
||||
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal(body, &raw); err != nil {
|
||||
return nil, errors.Join(ErrInvalidSESEvent, err)
|
||||
}
|
||||
|
||||
var event SESEventNotification
|
||||
if err := json.Unmarshal(body, &event); err != nil {
|
||||
return nil, errors.Join(ErrInvalidSESEvent, err)
|
||||
}
|
||||
if strings.TrimSpace(event.EventType) == "" {
|
||||
return nil, errors.Join(ErrInvalidSESEvent, errors.New("event type is required"))
|
||||
}
|
||||
if strings.TrimSpace(event.Mail.MessageID) == "" {
|
||||
return nil, errors.Join(ErrInvalidSESEvent, errors.New("mail message id is required"))
|
||||
}
|
||||
event.RawPayload = raw
|
||||
return &event, nil
|
||||
}
|
||||
|
||||
func unwrapSNSMessage(body []byte) ([]byte, bool) {
|
||||
var envelope snsEnvelope
|
||||
if err := json.Unmarshal(body, &envelope); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if strings.TrimSpace(envelope.Message) == "" {
|
||||
return nil, false
|
||||
}
|
||||
switch strings.TrimSpace(envelope.Type) {
|
||||
case "Notification", "":
|
||||
return []byte(envelope.Message), true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user