Conversation
…roviders, by_date endpoints Also adds api_query_params to RequestParams for automatic [] serialization of list query params. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR introduces a new StatsApi feature to the Mailtrap Python SDK for fetching and aggregating sending statistics with filtering capabilities. It includes new API endpoints, data models, automatic list parameter serialization, example usage, and comprehensive tests. Version bumped to 2.5.0. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client Code
participant MailtrapClient as MailtrapClient
participant GeneralApi as GeneralApi
participant StatsApi as StatsApi
participant HttpClient as HttpClient
participant MtApi as Mailtrap API
Client->>MailtrapClient: initialize with API token
Client->>MailtrapClient: general_api.stats
MailtrapClient->>GeneralApi: access stats property
GeneralApi->>StatsApi: create/return StatsApi instance
Client->>StatsApi: get(account_id, params)
activate StatsApi
StatsApi->>StatsApi: _base_path(account_id)
StatsApi->>HttpClient: GET /api/accounts/{id}/stats?params
deactivate StatsApi
HttpClient->>MtApi: GET request
MtApi-->>HttpClient: SendingStats JSON response
HttpClient-->>StatsApi: parsed response
StatsApi->>StatsApi: construct SendingStats(**data)
StatsApi-->>Client: SendingStats instance
Client->>StatsApi: by_domains(account_id, params)
activate StatsApi
StatsApi->>StatsApi: _grouped_stats(account_id, "domains", params)
StatsApi->>HttpClient: GET /api/accounts/{id}/stats/domains?params
deactivate StatsApi
HttpClient->>MtApi: GET request
MtApi-->>HttpClient: grouped stats JSON array
HttpClient-->>StatsApi: parsed response
loop For each item in response
StatsApi->>StatsApi: map to SendingStatGroup(name, value, stats)
end
StatsApi-->>Client: list[SendingStatGroup]
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
tests/unit/api/general/test_stats.py (1)
165-169: Assert the serialized list values, not just the key names.These checks still pass if the request encodes each list under the right key but with the wrong shape. Since this PR adds list-param serialization, it would be better to assert the exact repeated values in the query string.
Proposed test tightening
+from urllib.parse import parse_qs +from urllib.parse import urlparse + from typing import Any import pytest import responses @@ - request_params = responses.calls[0].request.params - assert "sending_domain_ids[]" in request_params - assert "sending_streams[]" in request_params - assert "categories[]" in request_params - assert "email_service_providers[]" in request_params + request_params = parse_qs(urlparse(responses.calls[0].request.url).query) + assert request_params["sending_domain_ids[]"] == ["1", "2"] + assert request_params["sending_streams[]"] == ["transactional"] + assert request_params["categories[]"] == ["Transactional"] + assert request_params["email_service_providers[]"] == ["Gmail"]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/api/general/test_stats.py` around lines 165 - 169, Update the assertions in the test that inspect responses.calls[0].request.params so they assert the serialized list values (not just the presence of keys); for example, replace the four assert "key in request_params" checks with assertions that request_params["sending_domain_ids[]"], request_params["sending_streams[]"], request_params["categories[]"], and request_params["email_service_providers[]"] equal the expected lists (or expected repeated values) used in the test input — this ensures the serialized shape and values produced by the list-param serialization logic (as observed via request_params) are exactly what the call should send.mailtrap/api/resources/stats.py (3)
1-4: Consider consolidating imports from the same module.The three imports from
mailtrap.models.statscan be combined into a single statement for cleaner code.♻️ Suggested consolidation
from mailtrap.http import HttpClient -from mailtrap.models.stats import SendingStatGroup -from mailtrap.models.stats import SendingStats -from mailtrap.models.stats import StatsFilterParams +from mailtrap.models.stats import SendingStatGroup, SendingStats, StatsFilterParams🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mailtrap/api/resources/stats.py` around lines 1 - 4, Consolidate the three separate imports from mailtrap.models.stats into a single import statement: replace the individual imports of SendingStatGroup, SendingStats, and StatsFilterParams with one grouped import from mailtrap.models.stats so the top of the file imports HttpClient from mailtrap.http and imports SendingStatGroup, SendingStats, StatsFilterParams together from mailtrap.models.stats.
6-11: Consider makingGROUP_KEYSa private constant.Since
GROUP_KEYSis an internal implementation detail used only by_grouped_stats, prefixing it with an underscore (_GROUP_KEYS) would signal this intent and prevent external reliance on the mapping.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mailtrap/api/resources/stats.py` around lines 6 - 11, Rename the public constant GROUP_KEYS to a private constant named _GROUP_KEYS and update all references to it (notably inside the _grouped_stats function) so the implementation detail is not exposed; ensure any import/usage within the same module uses _GROUP_KEYS and run tests to confirm there are no external references that need updating.
50-65: Consider stricter typing for thegroupparameter.The
groupparameter only accepts values fromGROUP_KEYS. Using aLiteraltype would provide better type safety and IDE support.♻️ Suggested type improvement
+from typing import Literal + +GroupType = Literal["domains", "categories", "email_service_providers", "date"] + def _grouped_stats( - self, account_id: int, group: str, params: StatsFilterParams + self, account_id: int, group: GroupType, params: StatsFilterParams ) -> list[SendingStatGroup]:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mailtrap/api/resources/stats.py` around lines 50 - 65, The group parameter to _grouped_stats only accepts keys from GROUP_KEYS, so declare a stricter Literal alias (e.g. GroupKey = Literal['keyA', 'keyB', ...] where the string literals enumerate the keys in GROUP_KEYS), replace the method signature to def _grouped_stats(self, account_id: int, group: GroupKey, params: StatsFilterParams) -> list[SendingStatGroup], and update any callers/type hints accordingly; keep the runtime behavior unchanged (still use GROUP_KEYS[group] and response parsing into SendingStatGroup).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/general/stats.py`:
- Line 7: ACCOUNT_ID is declared as a string but passed to helper functions
typed to accept int; change the declaration of ACCOUNT_ID from a string to an
integer literal (e.g., ACCOUNT_ID = 123456) and update any other sample
declarations/usages (the block around the later occurrences) so they pass an int
to the helper functions referenced in this file; ensure references to ACCOUNT_ID
in functions like the stats helpers now receive an int to keep types consistent.
---
Nitpick comments:
In `@mailtrap/api/resources/stats.py`:
- Around line 1-4: Consolidate the three separate imports from
mailtrap.models.stats into a single import statement: replace the individual
imports of SendingStatGroup, SendingStats, and StatsFilterParams with one
grouped import from mailtrap.models.stats so the top of the file imports
HttpClient from mailtrap.http and imports SendingStatGroup, SendingStats,
StatsFilterParams together from mailtrap.models.stats.
- Around line 6-11: Rename the public constant GROUP_KEYS to a private constant
named _GROUP_KEYS and update all references to it (notably inside the
_grouped_stats function) so the implementation detail is not exposed; ensure any
import/usage within the same module uses _GROUP_KEYS and run tests to confirm
there are no external references that need updating.
- Around line 50-65: The group parameter to _grouped_stats only accepts keys
from GROUP_KEYS, so declare a stricter Literal alias (e.g. GroupKey =
Literal['keyA', 'keyB', ...] where the string literals enumerate the keys in
GROUP_KEYS), replace the method signature to def _grouped_stats(self,
account_id: int, group: GroupKey, params: StatsFilterParams) ->
list[SendingStatGroup], and update any callers/type hints accordingly; keep the
runtime behavior unchanged (still use GROUP_KEYS[group] and response parsing
into SendingStatGroup).
In `@tests/unit/api/general/test_stats.py`:
- Around line 165-169: Update the assertions in the test that inspect
responses.calls[0].request.params so they assert the serialized list values (not
just the presence of keys); for example, replace the four assert "key in
request_params" checks with assertions that
request_params["sending_domain_ids[]"], request_params["sending_streams[]"],
request_params["categories[]"], and request_params["email_service_providers[]"]
equal the expected lists (or expected repeated values) used in the test input —
this ensures the serialized shape and values produced by the list-param
serialization logic (as observed via request_params) are exactly what the call
should send.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ae6b307f-d0d4-4d77-8389-241e8f13796b
📒 Files selected for processing (9)
CHANGELOG.mdexamples/general/stats.pymailtrap/__init__.pymailtrap/api/general.pymailtrap/api/resources/stats.pymailtrap/models/common.pymailtrap/models/stats.pypyproject.tomltests/unit/api/general/test_stats.py
| from mailtrap.models.stats import StatsFilterParams | ||
|
|
||
| API_TOKEN = "YOUR_API_TOKEN" | ||
| ACCOUNT_ID = "YOUR_ACCOUNT_ID" |
There was a problem hiding this comment.
Keep ACCOUNT_ID typed consistently with the helper signatures.
The sample declares ACCOUNT_ID as a string, then passes it unchanged into helpers that are typed as int. That makes the example internally inconsistent and noisy for type checkers.
Minimal fix
-ACCOUNT_ID = "YOUR_ACCOUNT_ID"
+ACCOUNT_ID = 123456 # replace with your account IDAlso applies to: 60-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/general/stats.py` at line 7, ACCOUNT_ID is declared as a string but
passed to helper functions typed to accept int; change the declaration of
ACCOUNT_ID from a string to an integer literal (e.g., ACCOUNT_ID = 123456) and
update any other sample declarations/usages (the block around the later
occurrences) so they pass an int to the helper functions referenced in this
file; ensure references to ACCOUNT_ID in functions like the stats helpers now
receive an int to keep types consistent.
| Documentation = "https://github.com/railsware/mailtrap-python" | ||
| Repository = "https://github.com/railsware/mailtrap-python.git" | ||
| "API documentation" = "https://api-docs.mailtrap.io/" | ||
| "API documentation" = "https://docs.mailtrap.io/developers" |
Motivation
Changes
How to test
Examples
Summary by CodeRabbit
New Features
Tests