Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Inbound Email:
- Inbound Inboxes API – [`inbound_inboxes_api.rb`](examples/inbound_inboxes_api.rb)
- Inbound Messages API (list, get, delete, reply, reply_all, forward) – [`inbound_messages_api.rb`](examples/inbound_messages_api.rb)
- Inbound Threads API – [`inbound_threads_api.rb`](examples/inbound_threads_api.rb)
- Inbound Forward Rules API – [`inbound_forward_rules_api.rb`](examples/inbound_forward_rules_api.rb)

Contact management:

Expand Down
34 changes: 34 additions & 0 deletions examples/inbound_forward_rules_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'mailtrap'

client = Mailtrap::Client.new(api_key: 'your-api-key')
forward_rules = Mailtrap::InboundForwardRulesAPI.new(client)
inbox_id = 42

# Create a forward rule
rule = forward_rules.create(
inbox_id,
name: 'Copy billing mail to finance',
conditions: [{ match_type: 'sender', operator: 'ends_with', value: '@billing.example.com' }],
destinations: [{ email: 'finance@example.com' }]
)
# => #<struct Mailtrap::InboundForwardRule id=7, name="Copy billing mail to finance", conditions=[...], ...>

# List forward rules
forward_rules.list(inbox_id)
# => [#<struct Mailtrap::InboundForwardRule id=7, ...>]

# Get a forward rule
forward_rules.get(inbox_id, rule.id)
# => #<struct Mailtrap::InboundForwardRule id=7, ...>

# Update a forward rule
forward_rules.update(
inbox_id,
rule.id,
destinations: [{ email: 'finance@example.com' }, { email: 'accounting@example.com' }]
)
# => #<struct Mailtrap::InboundForwardRule id=7, destinations=[...]>

# Delete a forward rule
forward_rules.delete(inbox_id, rule.id)
# => nil
3 changes: 3 additions & 0 deletions examples/inbound_messages_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
message = messages.get(inbox_id, page.data.first.id)
# => #<struct Mailtrap::InboundMessage id="1700000000000123", ...>

message.forwards
# => [#<struct Mailtrap::InboundForwardOutcome rule_id=7, destination="team@example.com", status="forwarded", ...>]

# Reply to the original sender
messages.reply(inbox_id, message.id, text: 'Thanks for reaching out.')
# => #<struct Mailtrap::InboundSendResult message_ids=["1a2b3c4d-..."]>
Expand Down
19 changes: 18 additions & 1 deletion examples/inbound_threads_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,27 @@
# Fetch the next page with the returned cursor
threads.list(inbox_id, last_id: page.last_id) if page.last_id

results = threads.list(inbox_id, search: 'acme')
# => #<struct Mailtrap::InboundThreadsListResponse data=[...], total_count=1, last_id=nil>

threads.list(inbox_id, search: 'acme', last_id: results.last_id) if results.last_id

# Get a single thread with its messages embedded (oldest first)
threads.get(inbox_id, page.data.first.id)
thread = threads.get(inbox_id, page.data.first.id)
# => #<struct Mailtrap::InboundThread id="1700000000000124", messages=[...]>

thread.messages.each do |message|
next unless message.visibility_status == 'available'

if message.direction == 'outbound'
message.delivery
# => #<struct Mailtrap::InboundThreadMessageDelivery to="customer@example.com", status="delivered", ...>
else
message.forwards
# => [#<struct Mailtrap::InboundForwardOutcome rule_id=7, destination="team@example.com", status="forwarded", ...>]
end
end

# Delete a thread
threads.delete(inbox_id, page.data.first.id)
# => nil
1 change: 1 addition & 0 deletions lib/mailtrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
require_relative 'mailtrap/inbound_inboxes_api'
require_relative 'mailtrap/inbound_messages_api'
require_relative 'mailtrap/inbound_threads_api'
require_relative 'mailtrap/inbound_forward_rules_api'

module Mailtrap
# @!macro api_errors
Expand Down
21 changes: 21 additions & 0 deletions lib/mailtrap/inbound_forward_outcome.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Mailtrap
# Data Transfer Object for an inbound forward outcome
# @see https://docs.mailtrap.io/developers/inbound
# @attr_reader rule_id [Integer] The forward rule ID
# @attr_reader rule_name [String, nil] The forward rule name
# @attr_reader destination [String] The destination address
# @attr_reader status [String] forwarded or rejected
# @attr_reader reason [String, nil] The rejection reason
# @attr_reader message_id [String, nil] The forwarded message ID
InboundForwardOutcome = Struct.new(
:rule_id,
:rule_name,
:destination,
:status,
:reason,
:message_id,
keyword_init: true
)
end
21 changes: 21 additions & 0 deletions lib/mailtrap/inbound_forward_rule.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Mailtrap
# Data Transfer Object for an inbound forward rule
# @see https://docs.mailtrap.io/developers/inbound
# @attr_reader id [Integer] The forward rule ID
# @attr_reader name [String] The rule name
# @attr_reader created_at [String] Creation timestamp
# @attr_reader updated_at [String] Last update timestamp
# @attr_reader conditions [Array<InboundForwardRuleCondition>] The rule conditions
# @attr_reader destinations [Array<InboundForwardRuleDestination>] The rule destinations
InboundForwardRule = Struct.new(
:id,
:name,
:created_at,
:updated_at,
:conditions,
:destinations,
keyword_init: true
)
end
17 changes: 17 additions & 0 deletions lib/mailtrap/inbound_forward_rule_condition.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Mailtrap
# Data Transfer Object for an inbound forward rule condition
# @see https://docs.mailtrap.io/developers/inbound
# @attr_reader match_type [String] The match type
# @attr_reader operator [String] The comparison operator
# @attr_reader value [String, nil] The value to compare against
# @attr_reader header_key [String, nil] The header name
InboundForwardRuleCondition = Struct.new(
:match_type,
:operator,
:value,
:header_key,
keyword_init: true
)
end
11 changes: 11 additions & 0 deletions lib/mailtrap/inbound_forward_rule_destination.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Mailtrap
# Data Transfer Object for an inbound forward rule destination
# @see https://docs.mailtrap.io/developers/inbound
# @attr_reader email [String] The destination email address
InboundForwardRuleDestination = Struct.new(
:email,
keyword_init: true
)
end
106 changes: 106 additions & 0 deletions lib/mailtrap/inbound_forward_rules_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# frozen_string_literal: true

require_relative 'base_api'
require_relative 'inbound_forward_rule'
require_relative 'inbound_forward_rule_condition'
require_relative 'inbound_forward_rule_destination'

module Mailtrap
class InboundForwardRulesAPI
include BaseAPI

self.supported_options = %i[name conditions destinations]
self.response_class = InboundForwardRule

# @param client [Mailtrap::Client] The client instance
def initialize(client = Mailtrap::Client.new)
@client = client
end

# Lists forward rules in an inbox
# @param inbox_id [Integer] The inbox ID
# @return [Array<InboundForwardRule>] Array of forward rules
# @!macro api_errors
def list(inbox_id)
response = client.get(forward_rules_path(inbox_id))
Array(response[:data]).map { |item| build_rule(item) }
end

# Retrieves a specific forward rule
# @param inbox_id [Integer] The inbox ID
# @param forward_rule_id [Integer] The forward rule ID
# @return [InboundForwardRule] Forward rule object
# @!macro api_errors
def get(inbox_id, forward_rule_id)
handle_response(client.get("#{forward_rules_path(inbox_id)}/#{forward_rule_id}"))
end

# Creates a new forward rule
# @param inbox_id [Integer] The inbox ID
# @param [Hash] options The parameters to create
# @option options [String] :name The rule name
# @option options [Array<Hash>] :conditions The rule conditions
# @option options [Array<Hash>] :destinations The rule destinations
# @return [InboundForwardRule] Created forward rule
# @!macro api_errors
# @raise [ArgumentError] If invalid options are provided
def create(inbox_id, options)
validate_options!(options, supported_options)
handle_response(client.post(forward_rules_path(inbox_id), options))
end

# Updates a forward rule
# @param inbox_id [Integer] The inbox ID
# @param forward_rule_id [Integer] The forward rule ID
# @param [Hash] options The parameters to update
# @option options [String] :name The rule name
# @option options [Array<Hash>] :conditions The rule conditions
# @option options [Array<Hash>] :destinations The rule destinations
# @return [InboundForwardRule] Updated forward rule
# @!macro api_errors
# @raise [ArgumentError] If invalid options are provided
def update(inbox_id, forward_rule_id, options)
validate_options!(options, supported_options)
handle_response(client.patch("#{forward_rules_path(inbox_id)}/#{forward_rule_id}", options))
end

# Deletes a forward rule
# @param inbox_id [Integer] The inbox ID
# @param forward_rule_id [Integer] The forward rule ID
# @return nil
# @!macro api_errors
def delete(inbox_id, forward_rule_id)
client.delete("#{forward_rules_path(inbox_id)}/#{forward_rule_id}")
end

private

def forward_rules_path(inbox_id)
"/api/inbound/inboxes/#{inbox_id}/forward_rules"
end

def handle_response(response)
build_rule(response[:data])
end

def build_rule(hash)
attrs = hash.slice(*InboundForwardRule.members)
attrs[:conditions] = build_conditions(attrs[:conditions]) if attrs[:conditions]
attrs[:destinations] = build_destinations(attrs[:destinations]) if attrs[:destinations]

InboundForwardRule.new(**attrs)
end

def build_conditions(conditions)
Array(conditions).map do |condition|
InboundForwardRuleCondition.new(**condition.slice(*InboundForwardRuleCondition.members))
end
end

def build_destinations(destinations)
Array(destinations).map do |destination|
InboundForwardRuleDestination.new(**destination.slice(*InboundForwardRuleDestination.members))
end
end
end
end
2 changes: 2 additions & 0 deletions lib/mailtrap/inbound_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module Mailtrap
# @attr_reader raw_message_expires_at [String, nil] When the raw message URL expires (only when fetched by ID)
# @attr_reader html_body [String, nil] Decoded HTML body (only when fetched by ID)
# @attr_reader text_body [String, nil] Decoded text body (only when fetched by ID)
# @attr_reader forwards [Array<InboundForwardOutcome>] The forward outcomes
# rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size
InboundMessage = Struct.new(
:id,
Expand All @@ -49,6 +50,7 @@ module Mailtrap
:raw_message_expires_at,
:html_body,
:text_body,
:forwards,
keyword_init: true
)
# rubocop:enable Lint/StructNewOverride
Expand Down
8 changes: 8 additions & 0 deletions lib/mailtrap/inbound_messages_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative 'base_api'
require_relative 'inbound_message'
require_relative 'inbound_attachment'
require_relative 'inbound_forward_outcome'
require_relative 'inbound_messages_list_response'
require_relative 'inbound_send_result'

Expand Down Expand Up @@ -100,6 +101,7 @@ def send_action(inbox_id, message_id, action, options)
def build_message(hash)
attrs = hash.slice(*InboundMessage.members)
attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments]
attrs[:forwards] = build_forwards(attrs[:forwards]) if attrs[:forwards]

InboundMessage.new(**attrs)
end
Expand All @@ -109,5 +111,11 @@ def build_attachments(attachments)
InboundAttachment.new(**attachment.slice(*InboundAttachment.members))
end
end

def build_forwards(forwards)
Array(forwards).map do |forward|
InboundForwardOutcome.new(**forward.slice(*InboundForwardOutcome.members))
end
end
end
end
13 changes: 5 additions & 8 deletions lib/mailtrap/inbound_thread_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

module Mailtrap
# Data Transfer Object for a message inside an inbound thread.
# Only +visibility_status+ and +direction+ are always present; +placeholder+ entries omit the rest,
# and the delivery lifecycle fields are only set on available outbound messages.
# Only +visibility_status+ and +direction+ are always present; +placeholder+ entries omit the rest.
# @see https://docs.mailtrap.io/developers/inbound
# @attr_reader visibility_status [String] available or placeholder
# @attr_reader direction [String] inbound or outbound
Expand All @@ -23,9 +22,8 @@ module Mailtrap
# @attr_reader text_body [String, nil] Decoded text body
# @attr_reader html_body [String, nil] Decoded HTML body
# @attr_reader attachments [Array<InboundAttachment>, nil] The message attachments
# @attr_reader delivery_status [String, nil] Delivery status (outbound messages only)
# @attr_reader delivered_at [String, nil] Delivery timestamp (outbound messages only)
# @attr_reader bounced_at [String, nil] Bounce timestamp (outbound messages only)
# @attr_reader delivery [InboundThreadMessageDelivery, nil] Delivery outcome (outbound messages only)
# @attr_reader forwards [Array<InboundForwardOutcome>, nil] Forward outcomes (inbound messages only)
InboundThreadMessage = Struct.new(
:visibility_status,
:direction,
Expand All @@ -45,9 +43,8 @@ module Mailtrap
:text_body,
:html_body,
:attachments,
:delivery_status,
:delivered_at,
:bounced_at,
:delivery,
:forwards,
keyword_init: true
)
end
17 changes: 17 additions & 0 deletions lib/mailtrap/inbound_thread_message_delivery.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Mailtrap
# Data Transfer Object for the delivery of an outbound message inside an inbound thread
# @see https://docs.mailtrap.io/developers/inbound
# @attr_reader to [String] The recipient address
# @attr_reader status [String] The delivery status
# @attr_reader delivered_at [String, nil] Delivery timestamp
# @attr_reader bounced_at [String, nil] Bounce timestamp
InboundThreadMessageDelivery = Struct.new(
:to,
:status,
:delivered_at,
:bounced_at,
keyword_init: true
)
end
Loading
Loading