Conversation
📝 WalkthroughWalkthroughAdds a new StatsApi to retrieve aggregated and grouped sending statistics (endpoints: get, byDomain, byCategory, byEmailServiceProvider, byDate). Includes types, API client, GeneralAPI integration, tests, example usage, and a CHANGELOG entry. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client Code
participant General as GeneralAPI
participant Stats as StatsApi
participant HTTP as Axios/HTTP Layer
participant API as Remote API
Client->>General: access general.stats
activate General
General->>General: checkAccountIdPresence()
General->>Stats: instantiate StatsApi(axios, accountId)
activate Stats
deactivate General
Client->>Stats: stats.get(filters)
activate Stats
Stats->>Stats: buildQueryParams(filters)
Stats->>HTTP: GET /api/accounts/{accountId}/stats?start_date=...&sending_domain_ids[]=...
activate HTTP
HTTP->>API: HTTP GET
activate API
API-->>HTTP: 200 with payload
deactivate API
HTTP-->>Stats: response.data
deactivate HTTP
Stats-->>Client: mapped SendingStats / groups
deactivate Stats
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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 (3)
examples/general/stats.ts (1)
87-92: Consider awaiting function calls or using an async IIFE.The example functions are called without
await, meaning errors won't be handled at the top level and execution order isn't guaranteed. For a cleaner example:Suggested fix
-testGetStats() -testGetStatsWithFilters() -testGetStatsByDomains() -testGetStatsByCategories() -testGetStatsByEmailServiceProviders() -testGetStatsByDate() +(async () => { + await testGetStats() + await testGetStatsWithFilters() + await testGetStatsByDomains() + await testGetStatsByCategories() + await testGetStatsByEmailServiceProviders() + await testGetStatsByDate() +})()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/general/stats.ts` around lines 87 - 92, The example calls to testGetStats, testGetStatsWithFilters, testGetStatsByDomains, testGetStatsByCategories, testGetStatsByEmailServiceProviders, and testGetStatsByDate are invoked without awaiting, so wrap these calls in an async IIFE or await them from an async function and handle errors; update the example to either (1) make the top-level module run an immediately-invoked async function that sequentially awaits each of the listed functions (testGetStats, testGetStatsWithFilters, testGetStatsByDomains, testGetStatsByCategories, testGetStatsByEmailServiceProviders, testGetStatsByDate) with try/catch around the sequence to surface errors, or (2) export/use an async main() that awaits each function and catches/logs errors before exiting.src/__tests__/lib/api/resources/Stats.test.ts (1)
70-91: Consider moving initialization tests afterbeforeAll.The
describe("class Stats(): ")block (lines 70-80) appears beforebeforeAll(lines 82-91). While this works because Jest hoistsbeforeAll, it's unconventional and could confuse readers. Consider movingbeforeAllto the top of the outerdescribeblock for clarity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/lib/api/resources/Stats.test.ts` around lines 70 - 91, Move the test setup so initialization runs before the spec declarations: relocate the beforeAll block (which initializes axios.interceptors.response.use, handleSendingError, and creates the AxiosMockAdapter mock) to the top of the outer describe that contains the "class Stats(): " tests so that beforeAll executes visually before the describe("class Stats(): ") block and the statsAPI initialization tests (expect(...).toHaveProperty(...)) appear after the test setup; keep references to axios.interceptors.response.use, handleSendingError, AxiosMockAdapter, mock, and statsAPI intact.src/lib/api/resources/Stats.ts (1)
75-94: Consider adding a guard for unknown group keys.While
groupedStatsis private and only called with valid keys, adding a guard would make the code more defensive against future changes:Optional defensive check
private async groupedStats( group: string, params: StatsFilterParams ): Promise<SendingStatGroup[]> { const url = `${this.statsURL}/${group}`; const groupKey = GROUP_KEYS[group]; + + if (!groupKey) { + throw new Error(`Unknown stats group: ${group}`); + } const response = await this.client.get<🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/api/resources/Stats.ts` around lines 75 - 94, Add a defensive guard in groupedStats: after computing const groupKey = GROUP_KEYS[group], verify groupKey is defined and handle the unknown case (e.g., throw a clear Error mentioning the invalid group or return an empty array) before using it to build the response; update groupedStats (and any callers if needed) so the function fails fast with a descriptive message referencing groupedStats and GROUP_KEYS when an invalid group is supplied.
🤖 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.ts`:
- Around line 4-5: The example defines ACCOUNT_ID as a string placeholder but
MailtrapClient expects a numeric accountId; change the placeholder to a numeric
literal (e.g., ACCOUNT_ID = 123456) or update usage to coerce/parse it to a
number before passing to MailtrapClient (reference ACCOUNT_ID and the
MailtrapClient constructor/accountId parameter) so the example shows a number
type rather than a string.
---
Nitpick comments:
In `@examples/general/stats.ts`:
- Around line 87-92: The example calls to testGetStats, testGetStatsWithFilters,
testGetStatsByDomains, testGetStatsByCategories,
testGetStatsByEmailServiceProviders, and testGetStatsByDate are invoked without
awaiting, so wrap these calls in an async IIFE or await them from an async
function and handle errors; update the example to either (1) make the top-level
module run an immediately-invoked async function that sequentially awaits each
of the listed functions (testGetStats, testGetStatsWithFilters,
testGetStatsByDomains, testGetStatsByCategories,
testGetStatsByEmailServiceProviders, testGetStatsByDate) with try/catch around
the sequence to surface errors, or (2) export/use an async main() that awaits
each function and catches/logs errors before exiting.
In `@src/__tests__/lib/api/resources/Stats.test.ts`:
- Around line 70-91: Move the test setup so initialization runs before the spec
declarations: relocate the beforeAll block (which initializes
axios.interceptors.response.use, handleSendingError, and creates the
AxiosMockAdapter mock) to the top of the outer describe that contains the "class
Stats(): " tests so that beforeAll executes visually before the describe("class
Stats(): ") block and the statsAPI initialization tests
(expect(...).toHaveProperty(...)) appear after the test setup; keep references
to axios.interceptors.response.use, handleSendingError, AxiosMockAdapter, mock,
and statsAPI intact.
In `@src/lib/api/resources/Stats.ts`:
- Around line 75-94: Add a defensive guard in groupedStats: after computing
const groupKey = GROUP_KEYS[group], verify groupKey is defined and handle the
unknown case (e.g., throw a clear Error mentioning the invalid group or return
an empty array) before using it to build the response; update groupedStats (and
any callers if needed) so the function fails fast with a descriptive message
referencing groupedStats and GROUP_KEYS when an invalid group is supplied.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b8d29381-21db-4838-bd09-f8a2d0532f47
📒 Files selected for processing (7)
CHANGELOG.mdexamples/general/stats.tssrc/__tests__/lib/api/resources/Stats.test.tssrc/index.tssrc/lib/api/General.tssrc/lib/api/resources/Stats.tssrc/types/api/stats.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.ts`:
- Around line 3-7: TEST_INBOX_ID is declared as a string but should be a number;
change the TEST_INBOX_ID constant to a numeric placeholder (e.g., 123456) and
ensure the MailtrapClient instantiation still passes testInboxId as that numeric
value so the testInboxId argument type matches what MailtrapClient expects in
its constructor.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9dedc9ac-4117-43ab-95ed-0612effa87a5
📒 Files selected for processing (7)
CHANGELOG.mdexamples/general/stats.tssrc/__tests__/lib/api/resources/Stats.test.tssrc/index.tssrc/lib/api/General.tssrc/lib/api/resources/Stats.tssrc/types/api/stats.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib/api/General.ts
examples/general/stats.ts
Outdated
| const TOKEN = "<YOUR-TOKEN-HERE>"; | ||
| const TEST_INBOX_ID = "<YOUR-TEST-INBOX-ID-HERE>" | ||
| const ACCOUNT_ID = 123456 | ||
|
|
||
| const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); |
There was a problem hiding this comment.
Type mismatch: TEST_INBOX_ID should be a number.
While ACCOUNT_ID was correctly updated to a numeric placeholder, TEST_INBOX_ID remains a string. Although testInboxId is optional and not used by the Stats API, keeping consistency helps users who copy the example.
Suggested fix
const TOKEN = "<YOUR-TOKEN-HERE>";
-const TEST_INBOX_ID = "<YOUR-TEST-INBOX-ID-HERE>"
+const TEST_INBOX_ID = 12345 // Replace with your test inbox ID
const ACCOUNT_ID = 123456📝 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.
| const TOKEN = "<YOUR-TOKEN-HERE>"; | |
| const TEST_INBOX_ID = "<YOUR-TEST-INBOX-ID-HERE>" | |
| const ACCOUNT_ID = 123456 | |
| const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); | |
| const TOKEN = "<YOUR-TOKEN-HERE>"; | |
| const TEST_INBOX_ID = 12345 // Replace with your test inbox ID | |
| const ACCOUNT_ID = 123456 | |
| const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/general/stats.ts` around lines 3 - 7, TEST_INBOX_ID is declared as a
string but should be a number; change the TEST_INBOX_ID constant to a numeric
placeholder (e.g., 123456) and ensure the MailtrapClient instantiation still
passes testInboxId as that numeric value so the testInboxId argument type
matches what MailtrapClient expects in its constructor.
ab2c7fe to
6e5276e
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/__tests__/lib/api/resources/Stats.test.ts (1)
70-79: Consider cleaning up interceptors inafterAll.The response interceptors added to the global axios instance in
beforeAllare not removed after tests complete. This could affect other test files if they share the same axios instance and don't expect these interceptors.♻️ Suggested cleanup
beforeAll(() => { /** * Init Axios interceptors for handling response.data, errors. */ - axios.interceptors.response.use( + const interceptorId = axios.interceptors.response.use( (response) => response.data, handleSendingError ); mock = new AxiosMockAdapter(axios); + return () => { + axios.interceptors.response.eject(interceptorId); + }; }); + +afterAll(() => { + mock.restore(); +});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/lib/api/resources/Stats.test.ts` around lines 70 - 79, The response interceptor added with axios.interceptors.response.use in beforeAll must be removed in afterAll to avoid leaking into other tests: capture the interceptor id when calling axios.interceptors.response.use((response)=>response.data, handleSendingError) and call axios.interceptors.response.eject(id) in afterAll; also restore the AxiosMockAdapter mock (mock.restore()) in afterAll to fully clean up the test fixture. Ensure the identifiers referenced are the interceptor id variable, axios.interceptors.response.use, axios.interceptors.response.eject, handleSendingError, and mock (AxiosMockAdapter) so the cleanup is applied to the same objects created in beforeAll.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/__tests__/lib/api/resources/Stats.test.ts`:
- Around line 70-79: The response interceptor added with
axios.interceptors.response.use in beforeAll must be removed in afterAll to
avoid leaking into other tests: capture the interceptor id when calling
axios.interceptors.response.use((response)=>response.data, handleSendingError)
and call axios.interceptors.response.eject(id) in afterAll; also restore the
AxiosMockAdapter mock (mock.restore()) in afterAll to fully clean up the test
fixture. Ensure the identifiers referenced are the interceptor id variable,
axios.interceptors.response.use, axios.interceptors.response.eject,
handleSendingError, and mock (AxiosMockAdapter) so the cleanup is applied to the
same objects created in beforeAll.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 446f146d-5eb8-4126-9357-874e19a6d778
📒 Files selected for processing (7)
CHANGELOG.mdexamples/general/stats.tssrc/__tests__/lib/api/resources/Stats.test.tssrc/index.tssrc/lib/api/General.tssrc/lib/api/resources/Stats.tssrc/types/api/stats.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/index.ts
- src/lib/api/General.ts
- src/types/api/stats.ts
Motivation
/api/accounts/{account_id}/stats) to the Node.js SDK, enabling users to retrieve aggregated email sending statistics.Changes
How to test
Examples
Summary by CodeRabbit
New Features
Documentation
Tests