Skip to content

Conversation

Ihor-Bilous
Copy link
Collaborator

@Ihor-Bilous Ihor-Bilous commented Aug 22, 2025

Motivation

In this PR I added ContactFieldsApi.

Changes

  • Added ContactsBaseApi , ContactFieldsApi, models, test, examples
  • Updated MailtrapClient

How to test

  • see examples/contacts/contact_fields.py

Images and GIFs

Summary by CodeRabbit

  • New Features
    • Introduced a Contacts API to manage contact fields (list, retrieve, create, update, delete).
    • Exposed contacts access from the client for account-scoped operations.
    • Added user-facing types for contact fields and parameter objects with validation for updates.
  • Documentation
    • Added an example demonstrating CRUD operations for contact fields using the SDK.
  • Tests
    • Added unit tests covering the contact fields API and parameter validation.

Copy link

coderabbitai bot commented Aug 22, 2025

Walkthrough

Adds contact field support: new Pydantic models, ContactFieldsApi resource with CRUD methods, a ContactsBaseApi facade, MailtrapClient.contacts_api accessor, package re-exports, an example script, and unit tests for API and models.

Changes

Cohort / File(s) Summary
Examples
examples/contacts/contact_fields.py
New example script demonstrating ContactField CRUD via a MailtrapClient instance and exposing helper functions plus a main listing.
Package Exports
mailtrap/__init__.py
Re-exports ContactField, CreateContactFieldParams, and UpdateContactFieldParams from mailtrap.models.contacts.
API Facade
mailtrap/api/contacts.py
Adds ContactsBaseApi storing HttpClient and account_id and exposing a contact_fields property returning ContactFieldsApi.
Resource API
mailtrap/api/resources/contact_fields.py
Adds ContactFieldsApi with get_list, get_by_id, create, update, and delete targeting /api/accounts/{account_id}/contacts/fields[/{id}].
Client Accessor
mailtrap/client.py
Adds MailtrapClient.contacts_api property that validates account_id and returns a ContactsBaseApi.
Models
mailtrap/models/contacts.py
Adds CreateContactFieldParams, UpdateContactFieldParams (validates at least one field), and ContactField dataclasses.
Tests: API
tests/unit/api/test_contacts.py
New unit tests for ContactFieldsApi covering success and 4xx error cases for list/get/create/update/delete.
Tests: Models
tests/unit/models/test_contacts.py
New unit tests for CreateContactFieldParams and UpdateContactFieldParams including api_data and validation behavior.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant Client as MailtrapClient
  participant Contacts as ContactsBaseApi
  participant Fields as ContactFieldsApi
  participant HTTP as HttpClient

  Dev->>Client: access contacts_api
  Client->>Contacts: instantiate with client, account_id
  Dev->>Contacts: access contact_fields
  Contacts->>Fields: instantiate with client, account_id

  rect rgba(200,230,255,0.25)
  Dev->>Fields: get_list() / get_by_id(id)
  Fields->>HTTP: GET /api/accounts/{account_id}/contacts/fields[/{id}]
  HTTP-->>Fields: JSON -> ContactField(s)
  Fields-->>Dev: ContactField or list[ContactField]
  end

  rect rgba(200,255,200,0.18)
  Dev->>Fields: create(params)
  Fields->>HTTP: POST .../fields (body: params.api_data)
  HTTP-->>Fields: 201 JSON -> ContactField
  Fields-->>Dev: ContactField
  end

  rect rgba(255,240,200,0.18)
  Dev->>Fields: update(id, params)
  Fields->>HTTP: PATCH .../fields/{id} (body)
  HTTP-->>Fields: JSON -> ContactField
  Fields-->>Dev: ContactField
  end

  rect rgba(255,220,220,0.18)
  Dev->>Fields: delete(id)
  Fields->>HTTP: DELETE .../fields/{id}
  HTTP-->>Fields: 204 No Content
  Fields-->>Dev: DeletedObject(id)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

enhancement

Suggested reviewers

  • VladimirTaytor
  • IgorDobryn
  • andrii-porokhnavets

Poem

I hop through fields of code and cheer,
New models, APIs, tests appear.
I nibble bugs and stamp my paw,
CRUD and facades—what a draw!
Hooray for contact fields, hip-hop hooray! 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ISSUE-19

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (9)
mailtrap/models/contacts.py (3)

8-13: CreateContactFieldParams looks good; consider constraining data_type

Implementation is straightforward and compatible with RequestParams.api_data. If the API accepts a closed set of data_type values (e.g., "integer", "string", "boolean", ...), consider Literal/Enum to catch mistakes early and improve editor assistance.


16-22: UpdateContactFieldParams validation: solid; optionally normalize blank input

The at-least-one check is correct and matches tests. To avoid sending empty strings (common 422s), trim values and treat blanks as None before the check.

Apply this diff to normalize and re-validate:

     def __post_init__(self) -> None:
-        if all(value is None for value in [self.name, self.merge_tag]):
+        # Normalize blank strings to None to avoid empty updates
+        if isinstance(self.name, str):
+            self.name = self.name.strip() or None
+        if isinstance(self.merge_tag, str):
+            self.merge_tag = self.merge_tag.strip() or None
+        if all(value is None for value in (self.name, self.merge_tag)):
             raise ValueError("At least one field must be provided for update action")

25-30: Consider making ContactField immutable

If ContactField is a response DTO, freezing helps prevent accidental mutation after parsing.

-@dataclass
+@dataclass(frozen=True)
 class ContactField:
tests/unit/models/test_contacts.py (1)

24-53: LGTM: UpdateContactFieldParams behavior covered; consider one more case

Great coverage: error on empty, partial update, and exclude_none serialization. Optionally add a case where only merge_tag is provided, and (if you accept the normalization diff) a case where whitespace-only values raise the same error.

mailtrap/client.py (1)

67-74: Error message context for missing account_id may confuse users

contacts_api reuses _validate_account_id(), which raises: "account_id is required for Testing API". This can mislead when accessing Contacts or Templates APIs.

Two low-risk options:

  • Generalize the message in _validate_account_id to "account_id is required" (may affect tests relying on the old string).
  • Or add an optional context parameter:
-    def _validate_account_id(self) -> None:
-        if not self.account_id:
-            raise ClientConfigurationError("`account_id` is required for Testing API")
+    def _validate_account_id(self, context: str = "") -> None:
+        if not self.account_id:
+            suffix = f" for {context}" if context else ""
+            raise ClientConfigurationError(f"`account_id` is required{suffix}")

…and call it here as _validate_account_id("Contacts API"). If you prefer not to change the signature, consider leaving as-is to keep parity with other properties.

mailtrap/__init__.py (1)

7-9: Re-exports trigger Ruff F401; declare them in all (preferred) or silence per-line

Static analysis flags these imports as unused, even though they’re intended for re-export.

Option A (preferred): ensure these names are in all.

 from .models.contacts import ContactField
 from .models.contacts import CreateContactFieldParams
 from .models.contacts import UpdateContactFieldParams
+
+# Keep __all__ authoritative for the public API surface
+try:
+    __all__
+except NameError:
+    __all__ = []
+__all__ += ["ContactField", "CreateContactFieldParams", "UpdateContactFieldParams"]

Option B (minimal): silence Ruff for these lines.

-from .models.contacts import ContactField
-from .models.contacts import CreateContactFieldParams
-from .models.contacts import UpdateContactFieldParams
+from .models.contacts import ContactField  # noqa: F401
+from .models.contacts import CreateContactFieldParams  # noqa: F401
+from .models.contacts import UpdateContactFieldParams  # noqa: F401
tests/unit/api/test_contacts.py (1)

96-101: Strengthen assertions for list mapping.

You only assert id. Since the response includes full field data, assert all mapped attributes to ensure model construction remains correct.

Apply this diff:

         contact_fields = contact_fields_api.get_list()
 
         assert isinstance(contact_fields, list)
         assert all(isinstance(f, ContactField) for f in contact_fields)
         assert contact_fields[0].id == FIELD_ID
+        assert contact_fields[0].name == "First name"
+        assert contact_fields[0].data_type == "text"
+        assert contact_fields[0].merge_tag == "first_name"
mailtrap/api/resources/contact_fields.py (1)

9-11: Constructor argument order consistency.

Other APIs typically accept account_id first, then client (and the facade uses keyword args). Consider normalizing the signature to __init__(self, account_id: str, client: HttpClient) for consistency. Since all current call sites use keywords, this is a safe change.

If you prefer, I can submit a follow-up patch across resources to standardize parameter order.

examples/contacts/contact_fields.py (1)

7-8: Nit: fix placeholder strings.

Use “YOUR_” in placeholders to avoid confusing readers.

Apply this diff:

-API_TOKEN = "YOU_API_TOKEN"
-ACCOUNT_ID = "YOU_ACCOUNT_ID"
+API_TOKEN = "YOUR_API_TOKEN"
+ACCOUNT_ID = "YOUR_ACCOUNT_ID"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d139f84 and 6e002ac.

📒 Files selected for processing (8)
  • examples/contacts/contact_fields.py (1 hunks)
  • mailtrap/__init__.py (1 hunks)
  • mailtrap/api/contacts.py (1 hunks)
  • mailtrap/api/resources/contact_fields.py (1 hunks)
  • mailtrap/client.py (2 hunks)
  • mailtrap/models/contacts.py (1 hunks)
  • tests/unit/api/test_contacts.py (1 hunks)
  • tests/unit/models/test_contacts.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
mailtrap/client.py (2)
mailtrap/api/contacts.py (1)
  • ContactsBaseApi (5-12)
mailtrap/http.py (1)
  • HttpClient (13-96)
mailtrap/api/resources/contact_fields.py (3)
mailtrap/http.py (4)
  • HttpClient (13-96)
  • get (25-29)
  • post (31-33)
  • patch (39-41)
mailtrap/models/common.py (2)
  • DeletedObject (22-23)
  • api_data (14-18)
mailtrap/models/contacts.py (3)
  • ContactField (26-30)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
tests/unit/models/test_contacts.py (2)
mailtrap/models/contacts.py (2)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
mailtrap/models/common.py (1)
  • api_data (14-18)
mailtrap/__init__.py (1)
mailtrap/models/contacts.py (3)
  • ContactField (26-30)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
examples/contacts/contact_fields.py (5)
mailtrap/models/common.py (1)
  • DeletedObject (22-23)
mailtrap/models/contacts.py (3)
  • ContactField (26-30)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
mailtrap/client.py (2)
  • MailtrapClient (24-142)
  • contacts_api (68-73)
mailtrap/api/contacts.py (1)
  • contact_fields (11-12)
mailtrap/api/resources/contact_fields.py (5)
  • create (23-28)
  • update (30-37)
  • get_list (13-15)
  • get_by_id (17-21)
  • delete (39-43)
tests/unit/api/test_contacts.py (6)
mailtrap/api/contacts.py (1)
  • contact_fields (11-12)
mailtrap/api/resources/contact_fields.py (6)
  • ContactFieldsApi (8-43)
  • get_list (13-15)
  • get_by_id (17-21)
  • create (23-28)
  • update (30-37)
  • delete (39-43)
mailtrap/exceptions.py (1)
  • APIError (10-15)
mailtrap/http.py (4)
  • HttpClient (13-96)
  • get (25-29)
  • post (31-33)
  • patch (39-41)
mailtrap/models/common.py (1)
  • DeletedObject (22-23)
mailtrap/models/contacts.py (3)
  • ContactField (26-30)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
mailtrap/models/contacts.py (1)
mailtrap/models/common.py (1)
  • RequestParams (12-18)
mailtrap/api/contacts.py (2)
mailtrap/api/resources/contact_fields.py (1)
  • ContactFieldsApi (8-43)
mailtrap/http.py (1)
  • HttpClient (13-96)
🪛 Ruff (0.12.2)
mailtrap/__init__.py

7-7: .models.contacts.ContactField imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


8-8: .models.contacts.CreateContactFieldParams imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


9-9: .models.contacts.UpdateContactFieldParams imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)

🔇 Additional comments (7)
tests/unit/models/test_contacts.py (1)

7-22: LGTM: api_data for CreateContactFieldParams

Test clearly asserts the serialized payload matches expectations.

mailtrap/client.py (1)

67-74: contacts_api accessor is consistent with existing client surface

Account validation + GENERAL_HOST HttpClient mirrors other APIs. Looks good.

mailtrap/api/contacts.py (1)

5-12: ContactsBaseApi is a clean, minimal facade

Constructor and contact_fields property are straightforward and align with the resource API. No issues.

tests/unit/api/test_contacts.py (1)

23-26: Fixture construction looks good.

Instantiating ContactFieldsApi with keyword args avoids the ctor-parameter-order issue and keeps the fixture readable.

mailtrap/api/resources/contact_fields.py (2)

39-43: Delete flow is sensible.

Returning a lightweight DeletedObject after a 204 aligns with the rest of the SDK patterns.


23-28: Bug: sending a method object instead of JSON payload (missing api_data() call).

field_params.api_data is a callable that must be invoked; otherwise requests receives an unserializable function object and the call breaks at runtime.

Apply this diff:

     def create(self, field_params: CreateContactFieldParams) -> ContactField:
         response = self._client.post(
             f"/api/accounts/{self._account_id}/contacts/fields",
-            json=field_params.api_data,
+            json=field_params.api_data(),
         )
         return ContactField(**response)
@@
         response = self._client.patch(
             f"/api/accounts/{self._account_id}/contacts/fields/{field_id}",
-            json=field_params.api_data,
+            json=field_params.api_data(),
         )
         return ContactField(**response)

Also applies to: 33-36

⛔ Skipped due to learnings
Learnt from: Ihor-Bilous
PR: railsware/mailtrap-python#34
File: mailtrap/api/resources/templates.py:33-36
Timestamp: 2025-08-21T10:37:54.214Z
Learning: In the mailtrap-python codebase, api_data is a property on ParametersObject (and classes that inherit from it), not a method. It should be accessed without parentheses: template_params.api_data, not template_params.api_data().
examples/contacts/contact_fields.py (1)

14-25: Example create flow looks correct.

Parameter construction and delegation to the API are straightforward.

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

♻️ Duplicate comments (1)
examples/contacts/contact_fields.py (1)

27-45: Good: IDs typed as int across helper functions

Aligns with API signatures and reduces misuse. This also addresses prior feedback.

🧹 Nitpick comments (5)
mailtrap/api/resources/projects.py (1)

41-43: Safer None-check for ID in path builder

Using “if project_id:” treats 0 as falsy. While IDs are typically positive, prefer an explicit None-check for correctness and consistency with future changes.

-        if project_id:
+        if project_id is not None:
             return f"{path}/{project_id}"
mailtrap/api/resources/templates.py (1)

45-47: Prefer explicit None-check in _api_path

Same rationale as for Projects/ContactFields: avoid truthiness checks on IDs.

-        if template_id:
+        if template_id is not None:
             return f"{path}/{template_id}"
mailtrap/api/resources/contact_fields.py (2)

25-31: Payload shape: confirm server expects flat body (not wrapped)

Templates use {"email_template": ...}, while ContactFields POST uses a flat payload (field_params.api_data). If the API expects wrapping (e.g., {"contact_field": ...}), this would fail at runtime.

If not already covered by unit tests against a mocked response, please confirm with the API docs or integration tests that POST/PUT/PATCH for contact fields are flat.

I can adjust the implementation once confirmed.


45-49: Explicit None-check in _api_path

Use is not None to avoid edge cases and match other resources.

-        if field_id:
+        if field_id is not None:
             return f"{path}/{field_id}"
examples/contacts/contact_fields.py (1)

7-11: Use env vars and fix placeholders for safer examples

Avoid hardcoding tokens and prefer YOUR_* placeholders. This prevents accidental credential leaks and mirrors common SDK examples.

-from mailtrap.models.common import DeletedObject
+from mailtrap.models.common import DeletedObject
+import os
@@
-API_TOKEN = "YOU_API_TOKEN"
-ACCOUNT_ID = "YOU_ACCOUNT_ID"
+API_TOKEN = os.getenv("MAILTRAP_API_TOKEN", "YOUR_API_TOKEN")
+ACCOUNT_ID = os.getenv("MAILTRAP_ACCOUNT_ID", "YOUR_ACCOUNT_ID")
@@
-client = mt.MailtrapClient(token=API_TOKEN, account_id=ACCOUNT_ID)
-contact_fields_api = client.contacts_api.contact_fields
+client = mt.MailtrapClient(token=API_TOKEN, account_id=ACCOUNT_ID)
+contact_fields_api = client.contacts_api.contact_fields
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6e002ac and bdfd9b3.

📒 Files selected for processing (4)
  • examples/contacts/contact_fields.py (1 hunks)
  • mailtrap/api/resources/contact_fields.py (1 hunks)
  • mailtrap/api/resources/projects.py (2 hunks)
  • mailtrap/api/resources/templates.py (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Ihor-Bilous
PR: railsware/mailtrap-python#35
File: examples/contacts/contact_fields.py:11-11
Timestamp: 2025-08-22T13:51:31.381Z
Learning: In mailtrap/api/contacts.py, ContactsBaseApi.contact_fields is defined as a property, not a method, so it can be accessed without parentheses like client.contacts_api.contact_fields.
Learnt from: Ihor-Bilous
PR: railsware/mailtrap-python#35
File: examples/contacts/contact_fields.py:11-11
Timestamp: 2025-08-22T13:51:31.381Z
Learning: In mailtrap/api/contacts.py, ContactsBaseApi.contact_fields is defined as a property, not a method, so it can be accessed without parentheses like client.contacts_api.contact_fields.
📚 Learning: 2025-08-22T13:51:31.381Z
Learnt from: Ihor-Bilous
PR: railsware/mailtrap-python#35
File: examples/contacts/contact_fields.py:11-11
Timestamp: 2025-08-22T13:51:31.381Z
Learning: In mailtrap/api/contacts.py, ContactsBaseApi.contact_fields is defined as a property, not a method, so it can be accessed without parentheses like client.contacts_api.contact_fields.

Applied to files:

  • examples/contacts/contact_fields.py
  • mailtrap/api/resources/contact_fields.py
🧬 Code graph analysis (4)
mailtrap/api/resources/projects.py (5)
mailtrap/http.py (4)
  • get (25-29)
  • post (31-33)
  • patch (39-41)
  • delete (43-45)
mailtrap/api/resources/contact_fields.py (5)
  • _api_path (45-49)
  • get_by_id (19-23)
  • create (25-30)
  • update (32-39)
  • delete (41-43)
mailtrap/api/resources/templates.py (5)
  • _api_path (43-47)
  • get_by_id (19-21)
  • create (23-28)
  • update (30-37)
  • delete (39-41)
mailtrap/models/projects.py (1)
  • Project (16-21)
mailtrap/models/common.py (1)
  • DeletedObject (22-23)
mailtrap/api/resources/templates.py (5)
mailtrap/http.py (3)
  • get (25-29)
  • post (31-33)
  • delete (43-45)
mailtrap/api/resources/contact_fields.py (4)
  • _api_path (45-49)
  • get_by_id (19-23)
  • create (25-30)
  • delete (41-43)
mailtrap/api/resources/projects.py (4)
  • _api_path (39-43)
  • get_by_id (17-19)
  • create (21-26)
  • delete (35-37)
mailtrap/models/templates.py (2)
  • EmailTemplate (40-49)
  • CreateEmailTemplateParams (9-14)
mailtrap/models/common.py (2)
  • api_data (14-18)
  • DeletedObject (22-23)
examples/contacts/contact_fields.py (4)
mailtrap/models/common.py (1)
  • DeletedObject (22-23)
mailtrap/models/contacts.py (3)
  • ContactField (26-30)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
mailtrap/client.py (1)
  • contacts_api (68-73)
mailtrap/api/resources/contact_fields.py (5)
  • create (25-30)
  • update (32-39)
  • get_list (15-17)
  • get_by_id (19-23)
  • delete (41-43)
mailtrap/api/resources/contact_fields.py (5)
mailtrap/http.py (4)
  • HttpClient (13-96)
  • get (25-29)
  • post (31-33)
  • patch (39-41)
mailtrap/models/common.py (2)
  • DeletedObject (22-23)
  • api_data (14-18)
mailtrap/models/contacts.py (3)
  • ContactField (26-30)
  • CreateContactFieldParams (9-12)
  • UpdateContactFieldParams (16-22)
mailtrap/api/resources/projects.py (6)
  • get_list (13-15)
  • _api_path (39-43)
  • get_by_id (17-19)
  • create (21-26)
  • update (28-33)
  • delete (35-37)
mailtrap/api/resources/templates.py (6)
  • get_list (15-17)
  • _api_path (43-47)
  • get_by_id (19-21)
  • create (23-28)
  • update (30-37)
  • delete (39-41)
⏰ 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). (6)
  • GitHub Check: Test python3.13 on windows-latest
  • GitHub Check: Test python3.12 on windows-latest
  • GitHub Check: Test python3.11 on windows-latest
  • GitHub Check: Test python3.9 on macos-latest
  • GitHub Check: Test python3.10 on windows-latest
  • GitHub Check: Test python3.9 on windows-latest
🔇 Additional comments (6)
mailtrap/api/resources/projects.py (1)

13-19: Nice refactor: centralized path construction improves maintainability

Replacing inline strings with _api_path() reduces duplication and the risk of drift across methods. Good consistency with other resources.

Also applies to: 21-33

mailtrap/api/resources/templates.py (1)

15-21: Good move: adopting _api_path across CRUD methods

The centralized path builder makes these methods leaner and less error-prone. Parity with Projects/ContactFields APIs looks solid.

Also applies to: 23-37

mailtrap/api/resources/contact_fields.py (3)

10-17: Solid resource scaffolding

Constructor and get_list mapping mirror existing patterns (Templates/Projects). Clear and consistent.


41-43: LGTM: delete semantics consistent with Templates

Ignoring the body and returning DeletedObject(field_id) matches HttpClient behavior for 204 responses and avoids None unpacking errors.


32-39: Review comment update: Pydantic dataclass __post_init__ guard is effective

The original comment assumed UpdateContactFieldParams was a Pydantic BaseModel, but it’s actually a Pydantic dataclass (imported via from pydantic.dataclasses import dataclass), so its __post_init__ method will be called after validation under Pydantic v2 and enforce the “at least one field” check as written (docs.pydantic.dev).

If you prefer to follow Pydantic v2 idiomatic hooks, you may optionally migrate this guard to a @model_validator(mode="after"), but it’s not required for correctness — __post_init__ in Pydantic dataclasses still runs after validation (docs.pydantic.dev).

Likely an incorrect or invalid review comment.

examples/contacts/contact_fields.py (1)

11-11: Correct: contact_fields is a property (no parentheses needed)

Matches ContactsBaseApi design. Thanks for keeping this ergonomic.

@Ihor-Bilous Ihor-Bilous merged commit d3cac02 into main Aug 25, 2025
19 checks passed
@Ihor-Bilous Ihor-Bilous deleted the ISSUE-19 branch August 25, 2025 12:50
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.

3 participants