Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis pull request introduces a new StatsApi resource to the Mailtrap client library, providing endpoints to retrieve sending statistics with optional filtering and grouping capabilities across multiple dimensions. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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
src/index.ts
Outdated
| import MailtrapTransport from "./lib/transport"; | ||
|
|
||
| export * from "./types/mailtrap"; | ||
| export * from "./types/api/stats"; |
There was a problem hiding this comment.
Hmm, we didn't export other api/ types like this, so this looks a bit off
There was a problem hiding this comment.
you are right, it was redundant - removed
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 `@src/lib/api/resources/Stats.ts`:
- Around line 100-122: The Mailtrap API expects array params serialized in
bracket notation, but Axios v0.27+ serializes arrays differently; update the
HTTP client configuration to use qs to serialize arrays as brackets and add qs
to package.json dependencies. Install and import qs, then configure the axios
instance's paramsSerializer to use qs.stringify(params, { arrayFormat:
'brackets' }) (ensure this is applied where the client/axios instance is created
that uses Stats.buildQueryParams), and keep buildQueryParams returning arrays
as-is so the serializer can handle them. Ensure package.json lists "qs" and run
install.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 53a989db-6a4b-413b-a76d-58f879fee700
📒 Files selected for processing (6)
CHANGELOG.mdexamples/general/stats.tssrc/__tests__/lib/api/resources/Stats.test.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 (2)
- examples/general/stats.ts
- src/tests/lib/api/resources/Stats.test.ts
| private static buildQueryParams( | ||
| params: StatsFilterParams | ||
| ): Record<string, unknown> { | ||
| const query: Record<string, unknown> = { | ||
| start_date: params.start_date, | ||
| end_date: params.end_date, | ||
| }; | ||
|
|
||
| if (params.sending_domain_ids) { | ||
| query.sending_domain_ids = params.sending_domain_ids; | ||
| } | ||
| if (params.sending_streams) { | ||
| query.sending_streams = params.sending_streams; | ||
| } | ||
| if (params.categories) { | ||
| query.categories = params.categories; | ||
| } | ||
| if (params.email_service_providers) { | ||
| query.email_service_providers = params.email_service_providers; | ||
| } | ||
|
|
||
| return query; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there's a custom paramsSerializer configured in the MailtrapClient or axios instance setup
rg -n "paramsSerializer" --type ts
# Check how other APIs in this codebase handle array parameters
rg -n -A5 "params:" src/lib/api/resources/*.ts | head -80
# Check if qs is already a dependency
cat package.json | jq '.dependencies, .devDependencies' | grep -i "qs"Repository: mailtrap/mailtrap-nodejs
Length of output: 4681
🏁 Script executed:
# Check axios version and if qs is a dependency
cat package.json | grep -A20 '"dependencies"' | head -30
cat package.json | grep -A20 '"devDependencies"' | head -30Repository: mailtrap/mailtrap-nodejs
Length of output: 1513
🏁 Script executed:
# Check the actual test file to see if it's passing or what it expects
fd -n "Stats.test.ts" -x cat -n {}Repository: mailtrap/mailtrap-nodejs
Length of output: 298
🏁 Script executed:
# Check if there's any axios configuration in the client setup
fd -n "MailtrapClient\|AxiosInstance\|createClient" -x rg -l {} \; 2>/dev/null | head -5Repository: mailtrap/mailtrap-nodejs
Length of output: 50
🏁 Script executed:
cat -n src/lib/api/resources/Stats.test.tsRepository: mailtrap/mailtrap-nodejs
Length of output: 135
🏁 Script executed:
# Also check the MailtrapClient setup to see how axios is configured
fd "MailtrapClient" -type f -x cat -n {}Repository: mailtrap/mailtrap-nodejs
Length of output: 238
🏁 Script executed:
# Find Stats test file
find . -name "*Stats*test*" -o -name "*test*Stats*" 2>/dev/null | head -10Repository: mailtrap/mailtrap-nodejs
Length of output: 115
🏁 Script executed:
# Find MailtrapClient file
find . -name "*MailtrapClient*" -o -name "*client*" -type f | grep -E "\.(ts|js)$" | head -10Repository: mailtrap/mailtrap-nodejs
Length of output: 426
🏁 Script executed:
# List test directory structure
find . -path "*/test*" -name "*.ts" | head -20Repository: mailtrap/mailtrap-nodejs
Length of output: 1131
🏁 Script executed:
cat -n ./src/__tests__/lib/api/resources/Stats.test.tsRepository: mailtrap/mailtrap-nodejs
Length of output: 10925
🏁 Script executed:
cat -n ./src/lib/MailtrapClient.tsRepository: mailtrap/mailtrap-nodejs
Length of output: 8738
🏁 Script executed:
# Check for any base API class that might configure axios
cat -n ./src/lib/api/*.ts | grep -A20 "paramsSerializer\|constructor"Repository: mailtrap/mailtrap-nodejs
Length of output: 8207
🏁 Script executed:
# Search for any global axios configuration or setup
grep -r "paramsSerializer" ./src --include="*.ts"Repository: mailtrap/mailtrap-nodejs
Length of output: 50
🏁 Script executed:
# Check if axios's getUri method has any special behavior documented
# Let's also look at how the Stats class receives axios instance
cat -n ./src/lib/api/resources/Stats.ts | head -40Repository: mailtrap/mailtrap-nodejs
Length of output: 1254
Configure custom array parameter serialization for Mailtrap API compatibility.
The test expects bracket notation (sending_domain_ids[]=1) for array parameters, but Axios's default serializer (v0.27+) produces indexed notation (sending_domain_ids[0]=1) or comma-separated values. Without explicit configuration, API requests with array filters will be malformed.
Add qs as a dependency and configure a custom paramsSerializer in the axios instance:
Required implementation
+import qs from "qs";
+
// In MailtrapClient.ts, line 69:
this.axios = axios.create({
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
headers: {
Authorization: `Bearer ${token}`,
Connection: "keep-alive",
"User-Agent": userAgent || USER_AGENT,
},
maxRedirects: MAX_REDIRECTS,
timeout: TIMEOUT,
+ paramsSerializer: (params) =>
+ qs.stringify(params, { arrayFormat: "brackets" }),
});Also add qs to package.json dependencies.
🤖 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 100 - 122, The Mailtrap API
expects array params serialized in bracket notation, but Axios v0.27+ serializes
arrays differently; update the HTTP client configuration to use qs to serialize
arrays as brackets and add qs to package.json dependencies. Install and import
qs, then configure the axios instance's paramsSerializer to use
qs.stringify(params, { arrayFormat: 'brackets' }) (ensure this is applied where
the client/axios instance is created that uses Stats.buildQueryParams), and keep
buildQueryParams returning arrays as-is so the serializer can handle them.
Ensure package.json lists "qs" and run install.
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