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
9 changes: 5 additions & 4 deletions examples/contacts_api.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
require 'mailtrap'

account_id = 3229
client = Mailtrap::Client.new(api_key: 'your-api-key')
contact_lists = Mailtrap::ContactListsAPI.new 3229, client
contacts = Mailtrap::ContactsAPI.new 3229, client
contact_fields = Mailtrap::ContactFieldsAPI.new 3229, client
contact_imports = Mailtrap::ContactImportsAPI.new 3229, client
contact_lists = Mailtrap::ContactListsAPI.new(account_id, client)
contacts = Mailtrap::ContactsAPI.new(account_id, client)
contact_fields = Mailtrap::ContactFieldsAPI.new(account_id, client)
contact_imports = Mailtrap::ContactImportsAPI.new(account_id, client)

# Set your API credentials as environment variables
# export MAILTRAP_API_KEY='your-api-key'
Expand Down
3 changes: 2 additions & 1 deletion examples/email_templates_api.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
require 'mailtrap'

account_id = 3229
client = Mailtrap::Client.new(api_key: 'your-api-key')
templates = Mailtrap::EmailTemplatesAPI.new 3229, client
templates = Mailtrap::EmailTemplatesAPI.new(account_id, client)

# Set your API credentials as environment variables
# export MAILTRAP_API_KEY='your-api-key'
Expand Down
40 changes: 40 additions & 0 deletions examples/suppressions_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'mailtrap'

account_id = 3229
client = Mailtrap::Client.new(api_key: 'your-api-key')
suppressions = Mailtrap::SuppressionsAPI.new(account_id, client)

# Set your API credentials as environment variables
# export MAILTRAP_API_KEY='your-api-key'
# export MAILTRAP_ACCOUNT_ID=your-account-id
#
# suppressions = Mailtrap::SuppressionsAPI.new

# Get all suppressions
suppressions.list
# =>
# [
# #<struct Mailtrap::Suppression
# id="64d71bf3-1276-417b-86e1-8e66f138acfe",
# type="unsubscription",
# created_at="2024-12-26T09:40:44.161Z",
# email="recipient@example.com",
# sending_stream="transactional",
# domain_name="example.com",
# message_bounce_category="",
# message_category="Welcome email",
# message_client_ip="123.123.123.123",
# message_created_at="2024-12-26T07:10:00.889Z",
# message_esp_response="",
# message_esp_server_type="",
# message_outgoing_ip="1.1.1.1",
# message_recipient_mx_name="Other Providers",
# message_sender_email="hello@sender.com",
# message_subject="Welcome!">
# ]

# Get suppressions for the email
list = suppressions.list(email: 'recipient@example.com')

# Delete a suppression
suppressions.delete(list.first.id)
Comment on lines +37 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against empty list before delete

list.first can be nil and raise NoMethodError on .id.

-# Delete a suppression
-suppressions.delete(list.first.id)
+# Delete a suppression (if any)
+if list.any?
+  suppressions.delete(list.first.id)
+else
+  warn 'No suppressions found for the given email'
+end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
list = suppressions.list(email: 'recipient@example.com')
# Delete a suppression
suppressions.delete(list.first.id)
list = suppressions.list(email: 'recipient@example.com')
# Delete a suppression (if any)
if list.any?
suppressions.delete(list.first.id)
else
warn 'No suppressions found for the given email'
end
🤖 Prompt for AI Agents
In examples/suppressions_api.rb around lines 37 to 40, calling list.first.id can
raise NoMethodError when the list is empty; update the code to check that the
returned list is non-empty (e.g., list.any? or !list.empty?) before attempting
to access first.id, and only call suppressions.delete when an item exists; if
the list is empty, either skip the delete or handle the case with a clear
message or error handling.

1 change: 1 addition & 0 deletions lib/mailtrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
require_relative 'mailtrap/contact_lists_api'
require_relative 'mailtrap/contact_fields_api'
require_relative 'mailtrap/contact_imports_api'
require_relative 'mailtrap/suppressions_api'

module Mailtrap
# @!macro api_errors
Expand Down
4 changes: 2 additions & 2 deletions lib/mailtrap/base_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ def base_delete(id)
client.delete("#{base_path}/#{id}")
end

def base_list
response = client.get(base_path)
def base_list(query_params = {})
response = client.get(base_path, query_params)
response.map { |item| handle_response(item) }
end

Expand Down
68 changes: 51 additions & 17 deletions lib/mailtrap/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,15 @@ def initialize( # rubocop:disable Metrics/ParameterLists
# @!macro api_errors
# @raise [Mailtrap::MailSizeError] If the message is too large.
def send_batch(base, requests)
perform_request(:post, api_host, batch_request_path, {
base:,
requests:
})
perform_request(
method: :post,
host: api_host,
path: batch_request_path,
body: {
base:,
requests:
}
)
end

# Sends an email
Expand Down Expand Up @@ -152,15 +157,26 @@ def send_batch(base, requests)
# @!macro api_errors
# @raise [Mailtrap::MailSizeError] If the message is too large
def send(mail)
perform_request(:post, api_host, send_path, mail)
perform_request(
method: :post,
host: api_host,
path: send_path,
body: mail
)
end

# Performs a GET request to the specified path
# @param path [String] The request path
# @param query_params [Hash] Query parameters to append to the URL (optional)
# @return [Hash, nil] The JSON response
# @!macro api_errors
def get(path)
perform_request(:get, general_api_host, path)
def get(path, query_params = {})
perform_request(
method: :get,
host: general_api_host,
path:,
query_params:
)
end

# Performs a POST request to the specified path
Expand All @@ -169,7 +185,12 @@ def get(path)
# @return [Hash, nil] The JSON response
# @!macro api_errors
def post(path, body = nil)
perform_request(:post, general_api_host, path, body)
perform_request(
method: :post,
host: general_api_host,
path:,
body:
)
end

# Performs a PATCH request to the specified path
Expand All @@ -178,15 +199,24 @@ def post(path, body = nil)
# @return [Hash, nil] The JSON response
# @!macro api_errors
def patch(path, body = nil)
perform_request(:patch, general_api_host, path, body)
perform_request(
method: :patch,
host: general_api_host,
path:,
body:
)
end

# Performs a DELETE request to the specified path
# @param path [String] The request path
# @return [Hash, nil] The JSON response
# @!macro api_errors
def delete(path)
perform_request(:delete, general_api_host, path)
perform_request(
method: :delete,
host: general_api_host,
path:
)
end

private
Expand All @@ -213,23 +243,27 @@ def batch_request_path
"/api/batch#{"/#{inbox_id}" if sandbox}"
end

def perform_request(method, host, path, body = nil)
def perform_request(method:, host:, path:, query_params: {}, body: nil)
http_client = http_client_for(host)
request = setup_request(method, path, body)

uri = URI::HTTPS.build(host:, path:)
uri.query = URI.encode_www_form(query_params) if query_params.any?

request = setup_request(method, uri, body)
response = http_client.request(request)
handle_response(response)
end

def setup_request(method, path, body = nil)
def setup_request(method, uri_or_path, body = nil)
request = case method
when :get
Net::HTTP::Get.new(path)
Net::HTTP::Get.new(uri_or_path)
when :post
Net::HTTP::Post.new(path)
Net::HTTP::Post.new(uri_or_path)
when :patch
Net::HTTP::Patch.new(path)
Net::HTTP::Patch.new(uri_or_path)
when :delete
Net::HTTP::Delete.new(path)
Net::HTTP::Delete.new(uri_or_path)
else
raise ArgumentError, "Unsupported HTTP method: #{method}"
end
Expand Down
46 changes: 46 additions & 0 deletions lib/mailtrap/suppression.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

module Mailtrap
# Data Transfer Object for Suppression
# @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/f8144826d885a-list-and-search-suppressions
# @attr_reader id [String] The suppression UUID
# @attr_reader type [String] The suppression type
# @attr_reader created_at [String] The creation timestamp
# @attr_reader email [String] The email address
# @attr_reader sending_stream [String] The sending stream
# @attr_reader domain_name [String, nil] The domain name
# @attr_reader message_bounce_category [String, nil] The bounce category
# @attr_reader message_category [String, nil] The message category
# @attr_reader message_client_ip [String, nil] The client IP
# @attr_reader message_created_at [String, nil] The message creation timestamp
# @attr_reader message_esp_response [String, nil] The ESP response
# @attr_reader message_esp_server_type [String, nil] The ESP server type
# @attr_reader message_outgoing_ip [String, nil] The outgoing IP
# @attr_reader message_recipient_mx_name [String, nil] The recipient MX name
# @attr_reader message_sender_email [String, nil] The sender email
# @attr_reader message_subject [String, nil] The message subject
Suppression = Struct.new(
:id,
:type,
:created_at,
:email,
:sending_stream,
:domain_name,
:message_bounce_category,
:message_category,
:message_client_ip,
:message_created_at,
:message_esp_response,
:message_esp_server_type,
:message_outgoing_ip,
:message_recipient_mx_name,
:message_sender_email,
:message_subject,
keyword_init: true
) do
# @return [Hash] The suppression attributes as a hash
def to_h
super.compact
end
end
end
37 changes: 37 additions & 0 deletions lib/mailtrap/suppressions_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require_relative 'base_api'
require_relative 'suppression'

module Mailtrap
class SuppressionsAPI
include BaseAPI

self.response_class = Suppression

# Lists all suppressions for the account
# @param email [String] Email address to filter suppressions (optional)
# @return [Array<Suppression>] Array of suppression objects
# @!macro api_errors
def list(email: nil)
query_params = {}
query_params[:email] = email if email

base_list(query_params)
end

# Deletes a suppression
# @param suppression_id [String] The suppression UUID
# @return nil
# @!macro api_errors
def delete(suppression_id)
client.delete("#{base_path}/#{suppression_id}")
end
Comment on lines +27 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Return type mismatch in docs vs implementation; validate input

YARD says “return nil” but the method returns whatever client.delete returns. Either return nil explicitly or update the doc. Also guard against blank IDs.

-    # @return nil
+    # @return [nil] Always nil on success
     # @!macro api_errors
     def delete(suppression_id)
-      client.delete("#{base_path}/#{suppression_id}")
+      raise ArgumentError, 'suppression_id is required' if suppression_id.to_s.empty?
+      client.delete("#{base_path}/#{suppression_id}")
+      nil
     end

If you prefer surfacing the HTTP response (e.g., status/boolean), update the YARD return type instead and keep the current behavior.


🏁 Script executed:

#!/bin/bash
echo "SuppressionsApi delete method docs and implementation:"
rg -nP -C2 "(#.*@return|def delete)" lib/mailtrap/suppressions_api.rb

echo -e "\nOther API delete methods:"
rg -nP -C3 "def delete" -g "lib/mailtrap/*_api.rb"

echo -e "\nClient.delete method:"
rg -nP -C2 "def delete" -g "lib/mailtrap/*client*.rb"

Length of output: 2985


🏁 Script executed:

#!/bin/bash
# Locate base_delete implementation to verify its behavior
rg -nP "def base_delete" -g "lib/mailtrap/**/*.rb" -C5

Length of output: 649


🏁 Script executed:

#!/bin/bash
# Show class definition of SuppressionsApi
rg -nC3 "class SuppressionsApi" -g "lib/mailtrap/suppressions_api.rb"

Length of output: 0


Update YARD @return to match actual return value

  • Change # @return nil to # @return [Hash, nil] since client.delete returns parsed JSON or nil
  • (Optional) Guard against blank suppression_id with
    raise ArgumentError, 'suppression_id is required' if suppression_id.to_s.empty?
🤖 Prompt for AI Agents
In lib/mailtrap/suppressions_api.rb around lines 27 to 29, the YARD return tag
is incorrect and should reflect that client.delete returns parsed JSON or nil;
update the method documentation from `# @return nil` to `# @return [Hash, nil]`
(or a more specific type if known), and optionally add a guard at the top of the
method to raise ArgumentError when suppression_id is blank (e.g. raise
ArgumentError, 'suppression_id is required' if suppression_id.to_s.empty?)
before calling client.delete.


private

def base_path
"/api/accounts/#{account_id}/suppressions"
end
end
end
Loading