Skip to content

Add support for Email Logs API#100

Open
mklocek wants to merge 1 commit intomainfrom
email-logs-api
Open

Add support for Email Logs API#100
mklocek wants to merge 1 commit intomainfrom
email-logs-api

Conversation

@mklocek
Copy link
Contributor

@mklocek mklocek commented Mar 13, 2026

Motivation

Add support for Email Logs API

Summary by CodeRabbit

  • New Features

    • Added Email Logs API for retrieving and listing email logs with filtering and pagination support.
  • Documentation

    • Updated README with Email Logs API entry.
    • Added example script demonstrating Email Logs API usage including filtering, pagination, and message retrieval.
  • Tests

    • Added comprehensive test coverage for Email Logs API functionality.

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

This PR introduces Email Logs API support to the Mailtrap Ruby library, adding a new API client for listing and retrieving email logs with filtering and cursor-based pagination, along with comprehensive data transfer objects and test coverage.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added Email Logs API entry to API list and supported functionality section.
Example Script
examples/email_logs_api.rb
New example demonstrating Email Logs API usage including list operations with filters, pagination via next_page_cursor, and single message retrieval with events.
Core Module Setup
lib/mailtrap.rb
Added require statement to load email_logs_api module.
API Client
lib/mailtrap/email_logs_api.rb
New EmailLogsAPI class providing list() and get() methods with query parameter building for filters and search_after pagination, response mapping to domain objects.
Data Transfer Objects
lib/mailtrap/email_log_message.rb, lib/mailtrap/email_logs_list_response.rb, lib/mailtrap/email_log_event.rb, lib/mailtrap/email_log_event_details.rb
Four new Struct-based DTOs for email log messages, list responses, events, and event-specific details (delivery, open, click, bounce, spam, unsubscribe, reject).
API Tests
spec/mailtrap/email_logs_api_spec.rb
Comprehensive test suite validating list operations with/without filters, pagination, single message retrieval, event mapping, and error handling for missing messages.
DTO Tests
spec/mailtrap/email_log_*.../spec.rb
Tests for EmailLogMessage, EmailLogEvent, and EmailLogEventDetails verifying proper initialization and struct mapping.
Test Fixtures
spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/...
Four VCR cassettes capturing list and get API interactions with various filter/pagination scenarios and error responses.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant EmailLogsAPI
    participant HTTPClient as HTTP Client
    participant APIServer as API Server
    participant Mapper as Response Mapper

    Client->>EmailLogsAPI: list(filters:, search_after:)
    EmailLogsAPI->>EmailLogsAPI: build_list_query_params(filters, search_after)
    EmailLogsAPI->>HTTPClient: GET /api/accounts/{id}/email_logs?filters[...]...
    HTTPClient->>APIServer: HTTP GET request
    APIServer-->>HTTPClient: 200 OK with messages array
    HTTPClient-->>EmailLogsAPI: Response body
    EmailLogsAPI->>Mapper: Map to EmailLogsListResponse
    Mapper->>Mapper: Build EmailLogMessage objects
    Mapper->>Mapper: Build EmailLogEvent & EmailLogEventDetails
    Mapper-->>EmailLogsAPI: EmailLogsListResponse instance
    EmailLogsAPI-->>Client: EmailLogsListResponse with messages, total_count, next_page_cursor
Loading
sequenceDiagram
    actor Client
    participant EmailLogsAPI
    participant HTTPClient as HTTP Client
    participant APIServer as API Server
    participant Mapper as Response Mapper

    Client->>EmailLogsAPI: get(sending_message_id)
    EmailLogsAPI->>HTTPClient: GET /api/accounts/{id}/email_logs/{message_id}
    HTTPClient->>APIServer: HTTP GET request
    alt Message Found
        APIServer-->>HTTPClient: 200 OK with message details
        HTTPClient-->>EmailLogsAPI: Response body
        EmailLogsAPI->>Mapper: Map to EmailLogMessage
        Mapper->>Mapper: Build EmailLogEvent array
        Mapper->>Mapper: Map event details to EmailLogEventDetails
        Mapper-->>EmailLogsAPI: EmailLogMessage with events
        EmailLogsAPI-->>Client: EmailLogMessage instance
    else Message Not Found
        APIServer-->>HTTPClient: 404 Not Found
        HTTPClient-->>EmailLogsAPI: Error response
        EmailLogsAPI-->>Client: Raise Mailtrap::Error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • i7an
  • DagonWat
  • IgorDobryn

Poem

🐰 Email logs flow like carrots in a row,
Filters bloom and paginate just so,
Events nested, details unfold,
New API tales of email gold!
✨🌱

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only includes the Motivation section and is largely incomplete, missing the Changes, How to test, and Images/GIFs sections required by the template. Complete the description by adding a Changes section listing the key additions (EmailLogsAPI class, DTOs, example), a How to test section with test checkboxes, and optionally an Images/GIFs section.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding support for the Email Logs API, which aligns perfectly with the changeset introducing EmailLogsAPI class, example code, and supporting data structures.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch email-logs-api
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mklocek mklocek marked this pull request as ready for review March 13, 2026 14:24
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
spec/mailtrap/email_log_message_spec.rb (1)

27-29: Test intent says “all attributes” but excludes optional fields.

Consider asserting raw_message_url and events explicitly (even as nil) so the claim and coverage match.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/mailtrap/email_log_message_spec.rb` around lines 27 - 29, The spec "it
'creates a message with all attributes'" currently uses expect(message).to
have_attributes(attributes) but the attributes hash omits optional fields;
update the test so the claim matches reality by adding raw_message_url and
events to the asserted attributes (set them to nil if that's the expected value)
or add explicit expectations like expect(message.raw_message_url).to be_nil and
expect(message.events).to be_nil; refer to the test subject `message`, the
example name, and the `attributes` hash when making the change.
examples/email_logs_api.rb (1)

3-11: Consider reordering to favor environment variables as the primary approach.

The example hardcodes credentials first, then shows the environment variable approach as a comment. For better security practices in example code, consider swapping these:

♻️ Suggested reordering
-account_id = 3229
-client = Mailtrap::Client.new(api_key: 'your-api-key')
-email_logs = Mailtrap::EmailLogsAPI.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
-#
-# email_logs = Mailtrap::EmailLogsAPI.new
+email_logs = Mailtrap::EmailLogsAPI.new
+
+# Or manually pass credentials:
+# account_id = 3229
+# client = Mailtrap::Client.new(api_key: 'your-api-key')
+# email_logs = Mailtrap::EmailLogsAPI.new(account_id, client)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/email_logs_api.rb` around lines 3 - 11, Swap the example to use
environment variables as the primary credential approach: instantiate
Mailtrap::Client and Mailtrap::EmailLogsAPI using ENV['MAILTRAP_API_KEY'] and
ENV['MAILTRAP_ACCOUNT_ID'] (or call Mailtrap::EmailLogsAPI.new with no args if
that constructor reads ENV) at the top, and move the hardcoded account_id and
Mailtrap::Client.new(api_key: 'your-api-key') example into a commented
“alternative” section; update the adjacent comments to show export instructions
and mention the hardcoded snippet as an optional fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/mailtrap/email_log_event_details.rb`:
- Around line 52-56: The build method in EmailLogEventDetails currently assumes
DETAIL_STRUCTS[event_type.to_s] exists and will raise NoMethodError when
event_type is nil or unknown; update EmailLogEventDetails.build to handle a
missing struct_class by checking the result of DETAIL_STRUCTS[event_type.to_s]
(and event_type.nil?) and either (a) return a sensible default/ fallback struct
(or a generic OpenStruct) populated with the sliced attrs, or (b) raise a clear
ArgumentError/RuntimeError with a descriptive message including the unknown
event_type; ensure you reference DETAIL_STRUCTS and struct_class in the change
so callers get a graceful/failing-but-descriptive behavior from
EmailLogEventDetails.build.

---

Nitpick comments:
In `@examples/email_logs_api.rb`:
- Around line 3-11: Swap the example to use environment variables as the primary
credential approach: instantiate Mailtrap::Client and Mailtrap::EmailLogsAPI
using ENV['MAILTRAP_API_KEY'] and ENV['MAILTRAP_ACCOUNT_ID'] (or call
Mailtrap::EmailLogsAPI.new with no args if that constructor reads ENV) at the
top, and move the hardcoded account_id and Mailtrap::Client.new(api_key:
'your-api-key') example into a commented “alternative” section; update the
adjacent comments to show export instructions and mention the hardcoded snippet
as an optional fallback.

In `@spec/mailtrap/email_log_message_spec.rb`:
- Around line 27-29: The spec "it 'creates a message with all attributes'"
currently uses expect(message).to have_attributes(attributes) but the attributes
hash omits optional fields; update the test so the claim matches reality by
adding raw_message_url and events to the asserted attributes (set them to nil if
that's the expected value) or add explicit expectations like
expect(message.raw_message_url).to be_nil and expect(message.events).to be_nil;
refer to the test subject `message`, the example name, and the `attributes` hash
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c132c193-ccee-4f99-a377-9cc692da37f6

📥 Commits

Reviewing files that changed from the base of the PR and between 457328b and 0c2db29.

📒 Files selected for processing (16)
  • README.md
  • examples/email_logs_api.rb
  • lib/mailtrap.rb
  • lib/mailtrap/email_log_event.rb
  • lib/mailtrap/email_log_event_details.rb
  • lib/mailtrap/email_log_message.rb
  • lib/mailtrap/email_logs_api.rb
  • lib/mailtrap/email_logs_list_response.rb
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_get/maps_response_data_to_EmailLogMessage_with_events_and_raw_message_url.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_get/when_message_not_found/raises_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_list/maps_response_data_to_EmailLogsListResponse_with_messages.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_list/with_filters_and_search_after/sends_correct_query_params_and_maps_response_data_to_EmailLogsListResponse_with_messages.yml
  • spec/mailtrap/email_log_event_details_spec.rb
  • spec/mailtrap/email_log_event_spec.rb
  • spec/mailtrap/email_log_message_spec.rb
  • spec/mailtrap/email_logs_api_spec.rb

Comment on lines +52 to +56
def self.build(event_type, hash)
struct_class = DETAIL_STRUCTS[event_type.to_s]
attrs = hash.slice(*struct_class.members)
struct_class.new(**attrs)
end
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

Handle unknown event types gracefully.

If event_type is nil or an unrecognized value (e.g., a new event type added by the API), struct_class will be nil, causing nil.members to raise NoMethodError.

🛡️ Proposed fix
     def self.build(event_type, hash)
       struct_class = DETAIL_STRUCTS[event_type.to_s]
+      return nil unless struct_class
+
       attrs = hash.slice(*struct_class.members)
       struct_class.new(**attrs)
     end

Alternatively, raise a descriptive error if failing loudly is preferred.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/mailtrap/email_log_event_details.rb` around lines 52 - 56, The build
method in EmailLogEventDetails currently assumes DETAIL_STRUCTS[event_type.to_s]
exists and will raise NoMethodError when event_type is nil or unknown; update
EmailLogEventDetails.build to handle a missing struct_class by checking the
result of DETAIL_STRUCTS[event_type.to_s] (and event_type.nil?) and either (a)
return a sensible default/ fallback struct (or a generic OpenStruct) populated
with the sliced attrs, or (b) raise a clear ArgumentError/RuntimeError with a
descriptive message including the unknown event_type; ensure you reference
DETAIL_STRUCTS and struct_class in the change so callers get a
graceful/failing-but-descriptive behavior from EmailLogEventDetails.build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant