Skip to content

Add missing endpoints#51

Open
IgorDobryn wants to merge 4 commits intomainfrom
MT-21864-add-missing-endpoints
Open

Add missing endpoints#51
IgorDobryn wants to merge 4 commits intomainfrom
MT-21864-add-missing-endpoints

Conversation

@IgorDobryn
Copy link
Copy Markdown

@IgorDobryn IgorDobryn commented May 4, 2026

Motivation

Changes

Contacts

  • GET /api/accounts/{account_id}/contacts/{contact_identifier} — Get contact

Webhooks

  • POST /api/accounts/{account_id}/webhooks — Create a webhook
  • GET /api/accounts/{account_id}/webhooks — List webhooks
  • GET /api/accounts/{account_id}/webhooks/{webhook_id} — Get a webhook
  • PATCH /api/accounts/{account_id}/webhooks/{webhook_id} — Update a webhook
  • DELETE /api/accounts/{account_id}/webhooks/{webhook_id} — Delete a webhook

API Tokens

  • GET /api/accounts/{account_id}/api_tokens — List API tokens
  • POST /api/accounts/{account_id}/api_tokens — Create API token
  • GET /api/accounts/{account_id}/api_tokens/{id} — Get API token
  • DELETE /api/accounts/{account_id}/api_tokens/{id} — Delete API token
  • POST /api/accounts/{account_id}/api_tokens/{id}/reset — Reset API token

Sub-Accounts (Organizations)

  • GET /api/organizations/{organization_id}/sub_accounts — Get organization sub-accounts
  • POST /api/organizations/{organization_id}/sub_accounts — Create organization sub-account

Summary by CodeRabbit

Release Notes

  • New Features

    • Added API token management capabilities: create, list, retrieve, delete, and reset tokens with resource-level access control.
    • Introduced webhook management: create, list, retrieve, update, and delete webhooks with support for multiple event types and payload formats.
    • Added sub-account management for organizations.
    • Enhanced contact retrieval by ID or email.
  • Bug Fixes

    • Improved handling of empty HTTP response bodies.
  • Chores

    • Updated environment configuration for service integration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

This PR adds three new API modules—API Tokens, Sub-Accounts, and Webhooks—each with interface, implementation, and request/response models. It updates the client factory and composition to wire these APIs, adds environment configuration for Mailtrap credentials, updates the contacts API with a new retrieval method, and includes comprehensive test coverage with fixtures.

Changes

API Tokens, Sub-Accounts, and Webhooks Features

Layer / File(s) Summary
Data Shape
src/main/java/io/mailtrap/model/request/apitokens/*, src/main/java/io/mailtrap/model/request/subaccounts/*, src/main/java/io/mailtrap/model/request/webhooks/*
src/main/java/io/mailtrap/model/response/apitokens/*, src/main/java/io/mailtrap/model/response/subaccounts/*, src/main/java/io/mailtrap/model/response/webhooks/*
src/main/java/io/mailtrap/model/WebhookEventType.java, WebhookPayloadFormat.java, WebhookType.java
src/main/java/io/mailtrap/model/ResourceType.java
Request/response DTOs introduced for token creation/retrieval, sub-account CRUD, and webhook management. New enums (WebhookEventType, WebhookPayloadFormat, WebhookType) and extended ResourceType to support webhook and token permission scoping.
API Contracts
src/main/java/io/mailtrap/api/apitokens/ApiTokens.java
src/main/java/io/mailtrap/api/subaccounts/SubAccounts.java
src/main/java/io/mailtrap/api/webhooks/Webhooks.java
Three new public interfaces define contract methods: API tokens (list, create, get, delete, reset), sub-accounts (list, create), and webhooks (create, list, get, update, delete).
Core Implementation
src/main/java/io/mailtrap/api/apitokens/ApiTokensImpl.java
src/main/java/io/mailtrap/api/subaccounts/SubAccountsImpl.java
src/main/java/io/mailtrap/api/webhooks/WebhooksImpl.java
Implementations extend ApiResource and wire HTTP calls to endpoints under /api/accounts and /api/organizations, handling request/response serialization via shared httpClient and the new model types.
Client Composition Wiring
src/main/java/io/mailtrap/client/MailtrapClient.java
src/main/java/io/mailtrap/client/api/MailtrapGeneralApi.java
src/main/java/io/mailtrap/client/api/MailtrapOrganizationsApi.java
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java
New MailtrapOrganizationsApi class holds SubAccounts dependency. MailtrapGeneralApi extended with ApiTokens and Webhooks fields. MailtrapClientFactory creates and injects implementations; MailtrapClient exposes organizationsApi getter.
Test Coverage
src/test/java/io/mailtrap/api/apitokens/ApiTokensImplTest.java
src/test/java/io/mailtrap/api/subaccounts/SubAccountsImplTest.java
src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java
src/test/resources/api/apitokens/*, src/test/resources/api/subaccounts/*, src/test/resources/api/webhooks/*
JUnit 5 tests with mocked HTTP client verify all operations (list/create/get/delete/reset/update) and assert expected response fields. JSON fixtures mock API responses.
Contact API Extension
src/main/java/io/mailtrap/api/contacts/Contacts.java
src/main/java/io/mailtrap/api/contacts/ContactsImpl.java
src/test/java/io/mailtrap/api/contacts/ContactsImplTest.java
src/test/resources/api/contacts/getContactResponse.json
Added getContact(accountId, idOrEmail) method to retrieve contact by ID or email with URL encoding support. Tests verify retrieval by email and UUID identifiers.
Infrastructure Updates
src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java
Empty 2xx response bodies now return null instead of attempting JSON deserialization.

Environment and Build Configuration

Layer / File(s) Summary
Configuration
.envrc
Replaced GPG_TTY and JAVA_TOOL_OPTIONS exports with four Mailtrap-specific environment variables (MAILTRAP_ACCOUNT_ID, MAILTRAP_ORGANIZATION_ID, MAILTRAP_API_KEY, MAILTRAP_ORGANIZATION_API_KEY) sourced from 1Password vault references.
VCS Ignore
.gitignore
Added .idea/ directory to ignore patterns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #36: Both add new API modules and update MailtrapClient/MailtrapClientFactory to inject additional API wrappers.
  • PR #33: Both modify MailtrapClientFactory to wire new API components into the client.
  • PR #39: Both modify ContactsImpl—this PR adds getContact method while the related PR applies parameter/encoding changes.

Suggested reviewers

  • mklocek
  • leonid-shevtsov
  • piobeny

🐰 Tokens hop and webhooks dance,
Sub-accounts join the spring advance,
With JSON fixtures, tests aligned,
A feature feast, so well-designed!
Mailtrap's API, now complete,
Makes rabbit engineers' hearts skip a beat! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add missing endpoints' directly and clearly summarizes the main change—adding multiple API endpoints across contacts, webhooks, API tokens, and sub-accounts.
Description check ✅ Passed The description follows the template structure with Motivation and Changes sections. All changes are comprehensively documented by endpoint group (Contacts, Webhooks, API Tokens, Sub-Accounts), though the How to test and Images sections are omitted.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MT-21864-add-missing-endpoints

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@IgorDobryn IgorDobryn changed the title Mt 21864 add missing endpoints Add missing endpoints May 4, 2026
@IgorDobryn IgorDobryn marked this pull request as ready for review May 4, 2026 13:24
Copy link
Copy Markdown

@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: 2

♻️ Duplicate comments (1)
src/test/resources/api/apitokens/createApiTokenResponse.json (1)

11-11: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Same access_level: 100 integer deserialization risk as in resetApiTokenResponse.json.

The access_level field here is also 100 (integer). The same @JsonCreator verification applies — see the comment raised on resetApiTokenResponse.json line 11.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/resources/api/apitokens/createApiTokenResponse.json` at line 11, The
test JSON uses "access_level": 100 (integer) which risks failing the model's
`@JsonCreator` string-validated deserialization (same issue noted in
resetApiTokenResponse.json); update
src/test/resources/api/apitokens/createApiTokenResponse.json to make
access_level a string value ("100") to match the `@JsonCreator` expectation, or
alternatively update the model's `@JsonCreator` (the deserializer that validates
the access_level field) to accept integers as well — reference the access_level
field and the `@JsonCreator` in the token response model when making the change.
🧹 Nitpick comments (5)
src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java (1)

9-13: 💤 Low value

Consider adding @ToString for logging/debugging support.

Using @Getter + @Setter`` instead of @Data is intentional and correct here — if a class has an explicit superclass, Lombok requires supplying a `callSuper` value for `@EqualsAndHashCode`; failure to do so results in a warning, which `@Data` would trigger. The only minor gap is that `@ToString` is not included, leaving the default `Object.toString()` (identity hash) for this request DTO, which can hinder logging.

💡 Suggested addition
 `@Getter`
 `@Setter`
 `@NoArgsConstructor`
 `@AllArgsConstructor`
+@ToString
 public class CreateSubAccountRequest extends AbstractModel {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java`
around lines 9 - 13, Add Lombok's `@ToString` to the CreateSubAccountRequest class
so instances produce meaningful string output for logging; annotate the class
with `@ToString` (and if AbstractModel contains relevant fields you want included
in logs, use `@ToString`(callSuper = true)) alongside the existing `@Getter/`@Setter
annotations on CreateSubAccountRequest.
src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java (1)

6-8: ⚡ Quick win

toString() omits all parent fields.

@ToString does not call super by default; callSuper = true must be set explicitly, and it's only meaningful when extending a non-Object class. Here @Data generates toString() that only includes token, silently dropping the six inherited fields (id, name, last4Digits, etc.). Since @EqualsAndHashCode(callSuper = true) is already present for consistency, add the matching @ToString override.

♻️ Proposed fix
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.ToString;

 `@Data`
 `@EqualsAndHashCode`(callSuper = true)
+@ToString(callSuper = true)
 public class ApiTokenWithToken extends ApiToken {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java`
around lines 6 - 8, The generated toString() on ApiTokenWithToken currently
omits all fields inherited from ApiToken; add an explicit Lombok ToString with
callSuper = true on the ApiTokenWithToken class (matching the existing
EqualsAndHashCode(callSuper = true)) so the superclass fields (id, name,
last4Digits, etc.) are included in the generated toString output.
src/test/resources/api/webhooks/createWebhookResponse.json (1)

14-14: 💤 Low value

Static analysis false positive — consider a scanner-friendly placeholder.

The flagged signing_secret value is clearly synthetic test data and poses no actual security risk. That said, the hex-pattern string will continue to trip secret scanners in CI. Using a non-hex-pattern string avoids that noise:

🔧 Suggested change to reduce scanner false positives
-    "signing_secret": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
+    "signing_secret": "test-signing-secret-placeholder"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/resources/api/webhooks/createWebhookResponse.json` at line 14,
Replace the synthetic hex-looking value for the JSON key "signing_secret" with a
scanner-friendly placeholder string (e.g., non-hex, clearly labeled test value
like "REDACTED_TEST_SIGNING_SECRET") so automated secret scanners stop flagging
it; update the value in the createWebhookResponse JSON where the
"signing_secret" key is defined and ensure the placeholder still clearly
indicates test-only usage.
src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java (1)

1-17: 💤 Low value

CreateWebhookRequest and UpdateWebhookRequest are structurally identical — consider a shared base.

Both classes extend AbstractModel, carry the same four Lombok annotations, and declare a single WebhookInput webhook field. A common abstract base would eliminate the duplication:

♻️ Proposed refactor
+ // new file: AbstractWebhookRequest.java
+ `@Getter`
+ `@Setter`
+ `@NoArgsConstructor`
+ `@AllArgsConstructor`
+ public abstract class AbstractWebhookRequest extends AbstractModel {
+     private WebhookInput webhook;
+ }
- `@Getter`
- `@Setter`
- `@NoArgsConstructor`
- `@AllArgsConstructor`
- public class CreateWebhookRequest extends AbstractModel {
-     private WebhookInput webhook;
- }
+ public class CreateWebhookRequest extends AbstractWebhookRequest { }
- `@Getter`
- `@Setter`
- `@NoArgsConstructor`
- `@AllArgsConstructor`
- public class UpdateWebhookRequest extends AbstractModel {
-     private WebhookInput webhook;
- }
+ public class UpdateWebhookRequest extends AbstractWebhookRequest { }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java`
around lines 1 - 17, Create a shared abstract base (e.g.,
AbstractWebhookRequest) that extends AbstractModel and declares the WebhookInput
webhook field plus the Lombok annotations (`@Getter`, `@Setter`, `@NoArgsConstructor`,
`@AllArgsConstructor`); then update CreateWebhookRequest and UpdateWebhookRequest
to simply extend that new base and remove the duplicated field and Lombok
annotations from those subclasses so only the unique class names remain (retain
reference to WebhookInput and AbstractModel).
src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java (1)

62-84: ⚡ Quick win

Consider adding a payloadFormat assertion to validate the new enum's round-trip deserialization.

WebhookPayloadFormat is introduced in this PR and its @JsonCreator path is exercised via fixtures (e.g., getWebhookResponse.json includes payload_format), but no test ever asserts the deserialized value. A single assertion in either test_createWebhook or test_getWebhook would confirm the @JsonValue/@JsonCreator wiring works end-to-end.

✅ Suggested assertion (e.g., in test_getWebhook)
 assertEquals(List.of(WebhookEventType.DELIVERY, WebhookEventType.BOUNCE), response.getData().getEventTypes());
+assertEquals(WebhookPayloadFormat.JSON, response.getData().getPayloadFormat());

Also applies to: 96-104

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java` around lines 62
- 84, Add an assertion to verify that the new enum WebhookPayloadFormat
round-trips correctly by asserting the deserialized payload format on the
response object; for example, in test_createWebhook (where CreateWebhookResponse
response is available) add an assertion that
response.getData().getPayloadFormat() equals WebhookPayloadFormat.JSON to
confirm the `@JsonValue/`@JsonCreator wiring works end-to-end.
🤖 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/main/java/io/mailtrap/model/WebhookEventType.java`:
- Around line 33-41: The fromValue method in WebhookEventType currently throws
IllegalArgumentException for unknown strings which will break deserialization of
List<WebhookEventType> eventTypes; change WebhookEventType.fromValue to handle
unknown values gracefully by returning either null or a dedicated UNKNOWN enum
constant instead of throwing—locate the static fromValue method in
WebhookEventType, mirror the fix used for WebhookType (check the loop over
WebhookEventType.values() and on no match return null or
WebhookEventType.UNKNOWN), and ensure callers that expect List<WebhookEventType>
can handle the null/UNKNOWN sentinel appropriately.

In `@src/main/java/io/mailtrap/model/WebhookType.java`:
- Around line 27-35: The current WebhookType.fromValue(String) method throws
IllegalArgumentException which breaks deserialization for unknown future enum
values; change the method to return a safe fallback instead of throwing: either
add an UNKNOWN sentinel constant to the WebhookType enum and return
WebhookType.UNKNOWN when no match is found, or return null from fromValue so
callers can handle absent values; update the `@JsonCreator` method
(WebhookType.fromValue) to implement the chosen fallback and apply the same
pattern to other enums using identical logic (e.g., WebhookPayloadFormat,
SendingStream, WebhookEventType) to ensure forward compatibility across the SDK.

---

Duplicate comments:
In `@src/test/resources/api/apitokens/createApiTokenResponse.json`:
- Line 11: The test JSON uses "access_level": 100 (integer) which risks failing
the model's `@JsonCreator` string-validated deserialization (same issue noted in
resetApiTokenResponse.json); update
src/test/resources/api/apitokens/createApiTokenResponse.json to make
access_level a string value ("100") to match the `@JsonCreator` expectation, or
alternatively update the model's `@JsonCreator` (the deserializer that validates
the access_level field) to accept integers as well — reference the access_level
field and the `@JsonCreator` in the token response model when making the change.

---

Nitpick comments:
In
`@src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java`:
- Around line 9-13: Add Lombok's `@ToString` to the CreateSubAccountRequest class
so instances produce meaningful string output for logging; annotate the class
with `@ToString` (and if AbstractModel contains relevant fields you want included
in logs, use `@ToString`(callSuper = true)) alongside the existing `@Getter/`@Setter
annotations on CreateSubAccountRequest.

In `@src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java`:
- Around line 1-17: Create a shared abstract base (e.g., AbstractWebhookRequest)
that extends AbstractModel and declares the WebhookInput webhook field plus the
Lombok annotations (`@Getter`, `@Setter`, `@NoArgsConstructor`, `@AllArgsConstructor`);
then update CreateWebhookRequest and UpdateWebhookRequest to simply extend that
new base and remove the duplicated field and Lombok annotations from those
subclasses so only the unique class names remain (retain reference to
WebhookInput and AbstractModel).

In `@src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java`:
- Around line 6-8: The generated toString() on ApiTokenWithToken currently omits
all fields inherited from ApiToken; add an explicit Lombok ToString with
callSuper = true on the ApiTokenWithToken class (matching the existing
EqualsAndHashCode(callSuper = true)) so the superclass fields (id, name,
last4Digits, etc.) are included in the generated toString output.

In `@src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java`:
- Around line 62-84: Add an assertion to verify that the new enum
WebhookPayloadFormat round-trips correctly by asserting the deserialized payload
format on the response object; for example, in test_createWebhook (where
CreateWebhookResponse response is available) add an assertion that
response.getData().getPayloadFormat() equals WebhookPayloadFormat.JSON to
confirm the `@JsonValue/`@JsonCreator wiring works end-to-end.

In `@src/test/resources/api/webhooks/createWebhookResponse.json`:
- Line 14: Replace the synthetic hex-looking value for the JSON key
"signing_secret" with a scanner-friendly placeholder string (e.g., non-hex,
clearly labeled test value like "REDACTED_TEST_SIGNING_SECRET") so automated
secret scanners stop flagging it; update the value in the createWebhookResponse
JSON where the "signing_secret" key is defined and ensure the placeholder still
clearly indicates test-only usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 382d1bdb-1ec4-4134-8033-69fdca8e920d

📥 Commits

Reviewing files that changed from the base of the PR and between e8a7ffc and 254252b.

📒 Files selected for processing (57)
  • .envrc
  • .gitignore
  • src/main/java/io/mailtrap/api/apitokens/ApiTokens.java
  • src/main/java/io/mailtrap/api/apitokens/ApiTokensImpl.java
  • src/main/java/io/mailtrap/api/contacts/Contacts.java
  • src/main/java/io/mailtrap/api/contacts/ContactsImpl.java
  • src/main/java/io/mailtrap/api/subaccounts/SubAccounts.java
  • src/main/java/io/mailtrap/api/subaccounts/SubAccountsImpl.java
  • src/main/java/io/mailtrap/api/webhooks/Webhooks.java
  • src/main/java/io/mailtrap/api/webhooks/WebhooksImpl.java
  • src/main/java/io/mailtrap/client/MailtrapClient.java
  • src/main/java/io/mailtrap/client/api/MailtrapGeneralApi.java
  • src/main/java/io/mailtrap/client/api/MailtrapOrganizationsApi.java
  • src/main/java/io/mailtrap/factory/MailtrapClientFactory.java
  • src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java
  • src/main/java/io/mailtrap/model/ResourceType.java
  • src/main/java/io/mailtrap/model/WebhookEventType.java
  • src/main/java/io/mailtrap/model/WebhookPayloadFormat.java
  • src/main/java/io/mailtrap/model/WebhookType.java
  • src/main/java/io/mailtrap/model/request/apitokens/ApiTokenResource.java
  • src/main/java/io/mailtrap/model/request/apitokens/CreateApiTokenRequest.java
  • src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java
  • src/main/java/io/mailtrap/model/request/subaccounts/SubAccountInput.java
  • src/main/java/io/mailtrap/model/request/webhooks/CreateWebhookRequest.java
  • src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java
  • src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java
  • src/main/java/io/mailtrap/model/response/apitokens/ApiToken.java
  • src/main/java/io/mailtrap/model/response/apitokens/ApiTokenResource.java
  • src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java
  • src/main/java/io/mailtrap/model/response/contacts/GetContactData.java
  • src/main/java/io/mailtrap/model/response/contacts/GetContactResponse.java
  • src/main/java/io/mailtrap/model/response/subaccounts/SubAccount.java
  • src/main/java/io/mailtrap/model/response/webhooks/CreateWebhookResponse.java
  • src/main/java/io/mailtrap/model/response/webhooks/ListWebhooksResponse.java
  • src/main/java/io/mailtrap/model/response/webhooks/Webhook.java
  • src/main/java/io/mailtrap/model/response/webhooks/WebhookResponse.java
  • src/main/java/io/mailtrap/model/response/webhooks/WebhookWithSecret.java
  • src/test/java/io/mailtrap/api/apitokens/ApiTokensImplTest.java
  • src/test/java/io/mailtrap/api/contacts/ContactsImplTest.java
  • src/test/java/io/mailtrap/api/subaccounts/SubAccountsImplTest.java
  • src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java
  • src/test/resources/api/apitokens/createApiTokenRequest.json
  • src/test/resources/api/apitokens/createApiTokenResponse.json
  • src/test/resources/api/apitokens/getApiTokenResponse.json
  • src/test/resources/api/apitokens/listApiTokensResponse.json
  • src/test/resources/api/apitokens/resetApiTokenResponse.json
  • src/test/resources/api/contacts/getContactResponse.json
  • src/test/resources/api/subaccounts/createSubAccountRequest.json
  • src/test/resources/api/subaccounts/createSubAccountResponse.json
  • src/test/resources/api/subaccounts/getSubAccountsResponse.json
  • src/test/resources/api/webhooks/createWebhookRequest.json
  • src/test/resources/api/webhooks/createWebhookResponse.json
  • src/test/resources/api/webhooks/deleteWebhookResponse.json
  • src/test/resources/api/webhooks/getWebhookResponse.json
  • src/test/resources/api/webhooks/listWebhooksResponse.json
  • src/test/resources/api/webhooks/updateWebhookRequest.json
  • src/test/resources/api/webhooks/updateWebhookResponse.json

Comment on lines +33 to +41
@JsonCreator
public static WebhookEventType fromValue(String value) {
for (WebhookEventType type : WebhookEventType.values()) {
if (type.value.equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("Unknown value: " + value);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Same forward-compatibility risk as WebhookType.fromValue.

The fromValue method throws IllegalArgumentException for any unknown event type. Because WebhookEventType is used in List<WebhookEventType> eventTypes, a single unrecognised value from the API will fail deserialization of the entire Webhook response. Apply the same null-return (or UNKNOWN sentinel) fix described for WebhookType.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/io/mailtrap/model/WebhookEventType.java` around lines 33 - 41,
The fromValue method in WebhookEventType currently throws
IllegalArgumentException for unknown strings which will break deserialization of
List<WebhookEventType> eventTypes; change WebhookEventType.fromValue to handle
unknown values gracefully by returning either null or a dedicated UNKNOWN enum
constant instead of throwing—locate the static fromValue method in
WebhookEventType, mirror the fix used for WebhookType (check the loop over
WebhookEventType.values() and on no match return null or
WebhookEventType.UNKNOWN), and ensure callers that expect List<WebhookEventType>
can handle the null/UNKNOWN sentinel appropriately.

Comment on lines +27 to +35
@JsonCreator
public static WebhookType fromValue(String value) {
for (WebhookType type : WebhookType.values()) {
if (type.value.equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("Unknown value: " + value);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check existing enum fromValue implementations for exception vs null/unknown fallback
rg -n "fromValue\|IllegalArgumentException\|UNKNOWN" --type java \
  -g "!WebhookType.java" -g "!WebhookEventType.java" \
  -A 5 -B 2

Repository: mailtrap/mailtrap-java

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Find all Java files that might be enums
fd -e java | xargs grep -l "enum\s\+\w\+\|@JsonCreator" | head -20

Repository: mailtrap/mailtrap-java

Length of output: 1424


🏁 Script executed:

#!/bin/bash
# Check the actual WebhookType.java file location and content
find . -name "WebhookType.java" -o -name "WebhookEventType.java" | xargs cat -n

Repository: mailtrap/mailtrap-java

Length of output: 2417


🏁 Script executed:

#!/bin/bash
# Look for all classes with `@JsonCreator` annotation
rg "@JsonCreator" --type java -B 2 -A 8

Repository: mailtrap/mailtrap-java

Length of output: 18300


Forward-compatibility risk: @JsonCreator throwing IllegalArgumentException will break deserialization of any new enum value from the API.

When @JsonCreator throws IllegalArgumentException, Jackson wraps it in an InvalidFormatException and the entire response deserialization fails. For a public SDK, where the Mailtrap API may evolve by adding new webhook types (e.g., transactional), all existing clients will crash at runtime with no graceful recovery.

This pattern is systemic across the codebase—nearly all enum classes (WebhookPayloadFormat, SendingStream, WebhookEventType, and 12+ others) use the same strict approach. Consider adopting a lenient fallback strategy:

  • Return null (callers must guard on null), or
  • Add an UNKNOWN sentinel constant and return it by default

Either approach maintains backward compatibility while allowing the SDK to gracefully handle future API evolution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/io/mailtrap/model/WebhookType.java` around lines 27 - 35, The
current WebhookType.fromValue(String) method throws IllegalArgumentException
which breaks deserialization for unknown future enum values; change the method
to return a safe fallback instead of throwing: either add an UNKNOWN sentinel
constant to the WebhookType enum and return WebhookType.UNKNOWN when no match is
found, or return null from fromValue so callers can handle absent values; update
the `@JsonCreator` method (WebhookType.fromValue) to implement the chosen fallback
and apply the same pattern to other enums using identical logic (e.g.,
WebhookPayloadFormat, SendingStream, WebhookEventType) to ensure forward
compatibility across the SDK.

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.

1 participant