Module: SuperSendTX::MessageMapper

Defined in:
lib/supersendtx/message_mapper.rb

Overview

Maps a Mail::Message into emails.send keyword args.

Constant Summary collapse

HEADER_IDEMPOTENCY =
"X-SuperSendTX-Idempotency-Key"
HEADER_SCHEDULED_AT =
"X-SuperSendTX-Scheduled-At"
HEADER_TAG =
"X-SuperSendTX-Tag"
RESERVED_HEADERS =
%w[
  from to cc bcc reply-to subject content-type mime-version date message-id
  x-supersendtx-idempotency-key x-supersendtx-scheduled-at x-supersendtx-tag
  idempotency-key
].freeze

Class Method Summary collapse

Class Method Details

.extract_forward_headers(mail) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/supersendtx/message_mapper.rb', line 124

def extract_forward_headers(mail)
  out = {}
  mail.header.fields.each do |field|
    name = field.name.to_s
    lower = name.downcase
    next if RESERVED_HEADERS.include?(lower)
    next if lower.start_with?("content-")

    value = field.value.to_s.strip
    out[name] = value unless value.empty?
  end
  out
end

.extract_tags(mail) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/supersendtx/message_mapper.rb', line 104

def extract_tags(mail)
  values = mail.header[HEADER_TAG]
  return [] unless values

  list = values.respond_to?(:map) ? values.map(&:to_s) : [values.to_s]
  list.filter_map do |raw|
    trimmed = raw.strip
    next if trimmed.empty?

    if trimmed.include?("=")
      name, value = trimmed.split("=", 2)
      next if name.strip.empty? || value.strip.empty?

      { "name" => name.strip, "value" => value.strip }
    else
      { "name" => "tag", "value" => trimmed }
    end
  end
end

.format_addresses(field) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/supersendtx/message_mapper.rb', line 65

def format_addresses(field)
  return [] if field.nil?

  Array(field.addrs).filter_map do |addr|
    email = addr.address.to_s.strip
    next if email.empty?

    name = addr.display_name.to_s.strip
    name.empty? ? email : "#{name} <#{email}>"
  end
end

.header_value(mail, name) ⇒ Object



138
139
140
141
142
143
144
145
# File 'lib/supersendtx/message_mapper.rb', line 138

def header_value(mail, name)
  field = mail.header[name]
  return nil unless field

  value = field.respond_to?(:last) ? field.last.to_s : field.to_s
  value = value.strip
  value.empty? ? nil : value
end

.html_body(mail) ⇒ Object



77
78
79
80
81
82
83
# File 'lib/supersendtx/message_mapper.rb', line 77

def html_body(mail)
  if mail.html_part
    mail.html_part.decoded
  elsif mail.mime_type == "text/html"
    mail.decoded
  end
end

.map_attachments(mail) ⇒ Object



94
95
96
97
98
99
100
101
102
# File 'lib/supersendtx/message_mapper.rb', line 94

def map_attachments(mail)
  mail.attachments.map do |attachment|
    {
      "filename" => attachment.filename.to_s.empty? ? "attachment" : attachment.filename,
      "content_type" => attachment.mime_type || "application/octet-stream",
      "content" => Base64.strict_encode64(attachment.body.decoded)
    }
  end
end

.text_body(mail) ⇒ Object



85
86
87
88
89
90
91
92
# File 'lib/supersendtx/message_mapper.rb', line 85

def text_body(mail)
  if mail.text_part
    mail.text_part.decoded
  elsif mail.mime_type == "text/plain" || (!mail.multipart? && mail.mime_type.nil?)
    body = mail.body.decoded
    body && !body.empty? ? body : nil
  end
end

.to_send_params(mail) ⇒ Object

Raises:

  • (ArgumentError)


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/supersendtx/message_mapper.rb', line 20

def to_send_params(mail)
  from = format_addresses(mail[:from])
  raise ArgumentError, "Email is missing a From address." if from.empty?

  to = format_addresses(mail[:to])
  raise ArgumentError, "Email is missing a To recipient." if to.empty?

  html = html_body(mail)
  text = text_body(mail)
  raise ArgumentError, "Email must include html or text content." if html.nil? && text.nil?

  params = {
    from: from.length == 1 ? from[0] : from,
    to: to.length == 1 ? to[0] : to
  }
  params[:subject] = mail.subject if mail.subject && !mail.subject.empty?
  params[:html] = html if html
  params[:text] = text if text

  reply_to = format_addresses(mail[:reply_to])
  params[:reply_to] = reply_to.length == 1 ? reply_to[0] : reply_to unless reply_to.empty?

  cc = format_addresses(mail[:cc])
  params[:cc] = cc unless cc.empty?
  bcc = format_addresses(mail[:bcc])
  params[:bcc] = bcc unless bcc.empty?

  attachments = map_attachments(mail)
  params[:attachments] = attachments unless attachments.empty?

  tags = extract_tags(mail)
  params[:tags] = tags unless tags.empty?

  headers = extract_forward_headers(mail)
  params[:headers] = headers unless headers.empty?

  idem = header_value(mail, HEADER_IDEMPOTENCY) || header_value(mail, "Idempotency-Key")
  params[:idempotency_key] = idem if idem

  scheduled = header_value(mail, HEADER_SCHEDULED_AT)
  params[:scheduled_at] = scheduled if scheduled

  params
end