Skip to content

Conversation

Ihor-Bilous
Copy link
Collaborator

@Ihor-Bilous Ihor-Bilous commented Oct 16, 2025

Motivation

In this PR I added ContactEventsApi, related models, tests, examples

How to test

  • see examples/contacts/contact_events.py

Summary by CodeRabbit

  • New Features

    • Added Contact Events support to create and manage contact events with name and custom parameters.
  • Documentation

    • Added an example script demonstrating how to create a contact event using the client.
  • Tests

    • Added unit tests covering contact event creation, success validation, and multiple error scenarios.

Copy link

coderabbitai bot commented Oct 16, 2025

Walkthrough

Adds contact event support: new models (ContactEventParams, ContactEvent), a ContactEventsApi resource with create(), a contact_events property on the contacts API, package export for ContactEventParams, unit tests, and an example script demonstrating event creation.

Changes

Cohort / File(s) Summary
API resource & integration
mailtrap/api/resources/contact_events.py, mailtrap/api/contacts.py
New ContactEventsApi class with create(contact_identifier, contact_event_params) and internal _api_path(); added contact_events property on ContactsBaseApi returning ContactEventsApi.
Data models
mailtrap/models/contacts.py
Added ContactEventParams and ContactEvent dataclasses (fields: name, params; contact_id, contact_email, name, params).
Package exports
mailtrap/__init__.py
Exported ContactEventParams at package level.
Tests
tests/unit/api/contacts/test_contact_events.py, tests/unit/models/test_contacts.py
Unit tests for ContactEventsApi.create() covering error and success cases; tests for ContactEventParams.api_data behavior.
Example
examples/contacts/contact_events.py
New example script showing MailtrapClient usage to create a contact event (create_contact_event function and a runnable main block).

Sequence Diagram

sequenceDiagram
    participant User
    participant Example as Example Script
    participant Client as MailtrapClient
    participant ContactsAPI as ContactsBaseApi
    participant ContactEventsAPI as ContactEventsApi
    participant HTTP as HttpClient
    participant API as Mailtrap API

    User->>Example: run script
    Example->>Client: initialize (API_TOKEN, ACCOUNT_ID)
    Example->>Client: access contacts_api
    Client->>ContactsAPI: return ContactsBaseApi
    Example->>ContactsAPI: access contact_events
    ContactsAPI->>ContactEventsAPI: instantiate ContactEventsApi
    Example->>ContactEventsAPI: create(contact_id, params)
    ContactEventsAPI->>HTTP: POST /api/send/contacts/{account_id}/contacts/{contact_id}/events with payload
    HTTP->>API: send request
    API-->>HTTP: 201 Created + response body
    HTTP->>ContactEventsAPI: return parsed response
    ContactEventsAPI->>Example: return ContactEvent instance
    Example->>User: print event
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 I bounced in with a tiny hop,
A model, an API, and a prop,
I stitched a path where events can play,
Now users' hops are saved each day! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is incomplete compared to the required template. While the description includes a "Motivation" section and "How to test" section, the critical "Changes" section is entirely absent. The Motivation section is also overly generic, simply repeating what the title states ("In this PR I added...") without explaining the business or technical rationale for these additions. The "How to test" section provides minimal detail, only pointing to an example file without structured testing instructions or checkboxes as specified in the template. Please enhance the description by adding a comprehensive "Changes" section that lists the key additions (e.g., ContactEventsApi class, ContactEventParams and ContactEvent models, new tests, public exports). Expand the "Motivation" section to explain why this API was needed and what problem it solves. Additionally, improve the "How to test" section by adding concrete testing steps with checkboxes as shown in the template, such as running unit tests and manually testing the example script.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Fix issue #45: Add ContactEventsApi, related models, tests and examples" is directly related to the main changes in the changeset. It clearly and specifically identifies that ContactEventsApi is being added along with related models, tests, and examples, which aligns perfectly with the file modifications across the codebase. The title is concise, single-sentence, and avoids vague language, making it easy for teammates scanning history to understand the primary change.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ISSUE-45

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.

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 (3)
tests/unit/api/contacts/test_contact_events.py (2)

15-15: Remove unused constant.

The EXPORT_ID constant is defined but never used in this test file.

Apply this diff to remove the unused constant:

 ACCOUNT_ID = "321"
-EXPORT_ID = 1
 CONTACT_IDENTIFIER = "d82d0d9e-bbb7-4656-a591-e64682bffae7"

126-126: Remove debug print statement.

The print statement appears to be debug code left in the test.

Apply this diff to remove it:

         with pytest.raises(APIError) as exc_info:
             client.create(CONTACT_IDENTIFIER, sample_create_contact_event_params)
 
-        print(str(exc_info.value))
-
         assert expected_error_message in str(exc_info.value)
mailtrap/api/resources/contact_events.py (1)

25-26: Clarify the optionality of contact_identifier.

The _api_path method accepts contact_identifier: Optional[str] = None, but the implementation uses it directly in the f-string without handling the None case. If None is passed, it would produce a path like /api/accounts/{id}/contacts/None/events.

Currently, the create method always passes a non-None value, so this isn't causing issues. However, for code clarity, consider either:

  1. Removing the Optional and default value if the parameter is always required
  2. Adding proper None handling if optionality is intended for future extensibility

Apply this diff to remove the unnecessary Optional:

-    def _api_path(self, contact_identifier: Optional[str] = None) -> str:
+    def _api_path(self, contact_identifier: str) -> str:
         return f"/api/accounts/{self._account_id}/contacts/{contact_identifier}/events"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a8fb79 and 57f8686.

📒 Files selected for processing (7)
  • examples/contacts/contact_events.py (1 hunks)
  • mailtrap/__init__.py (1 hunks)
  • mailtrap/api/contacts.py (2 hunks)
  • mailtrap/api/resources/contact_events.py (1 hunks)
  • mailtrap/models/contacts.py (2 hunks)
  • tests/unit/api/contacts/test_contact_events.py (1 hunks)
  • tests/unit/models/test_contacts.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
mailtrap/api/resources/contact_events.py (3)
mailtrap/http.py (2)
  • HttpClient (14-106)
  • post (32-34)
mailtrap/models/contacts.py (2)
  • ContactEvent (156-160)
  • ContactEventParams (150-152)
tests/unit/api/contacts/test_contact_events.py (1)
  • client (24-25)
mailtrap/__init__.py (1)
mailtrap/models/contacts.py (1)
  • ContactEventParams (150-152)
mailtrap/api/contacts.py (3)
mailtrap/api/resources/contact_events.py (1)
  • ContactEventsApi (8-26)
tests/unit/api/contacts/test_contact_events.py (1)
  • client (24-25)
tests/unit/api/contacts/test_contact_exports.py (1)
  • client (23-24)
examples/contacts/contact_events.py (4)
mailtrap/api/contacts.py (2)
  • contacts (36-37)
  • contact_events (16-17)
mailtrap/models/contacts.py (2)
  • ContactEvent (156-160)
  • ContactEventParams (150-152)
mailtrap/client.py (1)
  • MailtrapClient (31-170)
mailtrap/api/resources/contact_events.py (1)
  • create (13-23)
tests/unit/api/contacts/test_contact_events.py (4)
mailtrap/api/resources/contact_events.py (2)
  • ContactEventsApi (8-26)
  • create (13-23)
mailtrap/exceptions.py (1)
  • APIError (10-15)
mailtrap/http.py (2)
  • HttpClient (14-106)
  • post (32-34)
mailtrap/models/contacts.py (2)
  • ContactEvent (156-160)
  • ContactEventParams (150-152)
tests/unit/models/test_contacts.py (2)
mailtrap/models/contacts.py (1)
  • ContactEventParams (150-152)
mailtrap/models/common.py (1)
  • api_data (15-19)
mailtrap/models/contacts.py (1)
mailtrap/models/common.py (1)
  • RequestParams (13-19)
🪛 Ruff (0.14.0)
examples/contacts/contact_events.py

4-4: Possible hardcoded password assigned to: "API_TOKEN"

(S105)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test python3.10 on windows-latest

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: 0

🧹 Nitpick comments (1)
mailtrap/models/contacts.py (1)

155-160: Consider more restrictive typing for params.

The ContactEvent model structure looks correct as a response model. However, the params field uses dict[str, Any], which is more permissive than the Union[str, int, float, bool] used for similar dictionary fields elsewhere in this file (e.g., Contact.fields, CreateContactParams.fields).

Using Any provides maximum flexibility but sacrifices type safety. Consider whether the more restrictive union type would be appropriate, or if the broader Any type is intentionally needed to support complex nested structures (lists, nested dicts, etc.).

If you prefer stricter typing, apply this diff:

 @dataclass
 class ContactEvent:
     contact_id: str
     contact_email: str
     name: str
-    params: Optional[dict[str, Any]] = None
+    params: Optional[dict[str, Union[str, int, float, bool]]] = None

Note: Apply the same change to ContactEventParams (line 152) if you choose this approach for consistency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57f8686 and cf58cda.

📒 Files selected for processing (2)
  • mailtrap/models/contacts.py (2 hunks)
  • tests/unit/models/test_contacts.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/models/test_contacts.py
🧰 Additional context used
🧬 Code graph analysis (1)
mailtrap/models/contacts.py (1)
mailtrap/models/common.py (1)
  • RequestParams (13-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test python3.13 on macos-latest
  • GitHub Check: Test python3.12 on windows-latest
  • GitHub Check: Test python3.9 on windows-latest
  • GitHub Check: Test python3.11 on windows-latest
  • GitHub Check: Test python3.10 on windows-latest
  • GitHub Check: Test python3.9 on macos-latest
  • GitHub Check: Test python3.13 on windows-latest
🔇 Additional comments (2)
mailtrap/models/contacts.py (2)

3-3: LGTM!

The Any import is necessary for the dict[str, Any] type annotations in the new dataclasses.


149-152: LGTM!

The ContactEventParams class correctly inherits from RequestParams and follows the established pattern for request parameter classes. The structure is appropriate with a required name field and an optional params dictionary for flexible event parameters.

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.

2 participants