Skip to content

MSD-15205: Review SDKs (recurring) - #146

Open
odunayoogungbure-rgb wants to merge 5 commits into
mainfrom
claude/audit-sdk-coverage-fa9d0f
Open

odunayoogungbure-rgb wants to merge 5 commits into
mainfrom
claude/audit-sdk-coverage-fa9d0f

Conversation

@odunayoogungbure-rgb

@odunayoogungbure-rgb odunayoogungbure-rgb commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes for the gaps an audit of every endpoint against the API reference found. Only the fixes and the tests that cover them.

Changes

  • Email: empty lists are no longer sent (references: [] was rejected on the Sandbox plan), nor rcpt_to or the extra sendAt key
  • Bulk email: bulkSend() sends send_at; bulk status handles validation_errors: null, keyed messages_id and 404
  • Users: invite and update responses are read unwrapped (update threw a NullPointerException); empty permissions/templates/domains are no longer sent
  • Async email verification reads its fields from data; the verification builder clears its emails after create()
  • Blocklist monitoring: the integer blocklisted count no longer crashes Gson (blocklistedCount added, blocklisted kept)
  • SMS: webhook update no longer wipes events; inbound routes are no longer created or updated as disabled; single messages read the sms array
  • Domains and SMTP users: verified sent as 1/0, SMTP user domain read correctly, date fields parsed
  • Templates: subject no longer sent, auto_generate not sent on create, missing model fields added, getTemplate() no longer crashes on totals: []
  • Inbound routes: fields the API doesn't accept are no longer sent; catch_type/match_type read
  • Query values URL-encoded for DMARC and identity filters and analytics tags
  • Token update body built with Gson (escaping); token models get domain and the missing create fields
  • Added the 3 missing webhook events, WhatsApp media headers and named variables
  • getActivities(domainId) requests the last 7 days instead of always throwing
  • Old names kept as deprecated aliases (e.g. customnTrackingEnabled → customTrackingEnabled)
  • README: examples that didn't compile are fixed

Risks

  • bulkSend() with setSendAt() now schedules the emails; before, they went out immediately
  • SMS inbound routes created without .enabled() are now enabled (the API default)
  • SmsInboundRouteBuilderBody.enabled is now Boolean, which only affects code compiled against that field

Notes

Dropping rcpt_to is safe. The API has no rcpt_to field (it only reads camelCase rcptTo, and that is for SMTP relay), so validation discarded the value and those addresses never received anything. Removing it doesn't change who an email reaches.

How to QA

mvn test: the 45 tests this PR adds pass. On Linux, main already has 114 failing tests because fixture file names differ from test names only in letter case, and testFailureIgnore hides them. This PR leaves that as it is.

Fixes from an audit of every endpoint against the API docs.

- Email: stop sending empty lists (`references: []` failed on Sandbox),
  `rcpt_to` and the extra `sendAt` key; bulk send now honours `send_at`
- Bulk status: handle `validation_errors: null`, keyed `messages_id` and 404
- Users: read invite/update responses unwrapped; stop sending empty
  permissions/templates/domains
- Async email verification: read fields from `data`
- Blocklist monitoring: read the integer `blocklisted` count without crashing
- SMS: don't wipe webhook events on update; don't disable inbound routes;
  read the `sms` array on single messages
- Domains/SMTP users: send `verified` as 1/0, read `domain`, parse dates
- URL-encode query values (DMARC dates, identity and blocklist queries, tags)
- Add missing webhook events and WhatsApp media headers/named variables
- Keep old names as deprecated aliases (e.g. `customnTrackingEnabled`)
- Fixtures: match test names exactly (Linux CI is case-sensitive), replace
  recorded 401s, redact a token in an old recording; fix README examples

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@odunayoogungbure-rgb odunayoogungbure-rgb self-assigned this Oct 23, 2026
odunayoogungbure-rgb and others added 4 commits October 27, 2026 15:28
Resolve README conflicts with the WhatsApp read endpoints: keep main's
top-level WhatsApp section and its new API sections, add the media header
and named variables examples and the four sender forms for `from`, and
keep the blocklist monitoring contents entry only once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep only the fixes for audit-flagged gaps and the tests that cover them.
Put the case-only fixture renames back under their original names, restore
deleted fixtures, and revert test restructuring, tests on unchanged code,
the token name/status checks, fixes outside the audit and README polish.
Keep the token redaction in the TokensTest create fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
javac warned that members with a @deprecated Javadoc tag lacked the
annotation. Add it to the members this branch deprecates and to the
existing Email Add* aliases in the same file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clears the last "deprecated item is not annotated" javac warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@odunayoogungbure-rgb
odunayoogungbure-rgb marked this pull request as ready for review October 27, 2026 17:23
@mailerlite-kritika

mailerlite-kritika Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Re-runViewConfidence Score: 4/5

[High risk] The API-acceptance claims behind the dropped fields (rcpt_to, inbound address and subdomain, forward secret, editable, auto_generate, subject) rest on the author's audit and the description, and the review's only outside source was a PHP SDK code search, so nothing it read confirms them. The risk is high because the change alters public SDK surfaces (deprecated and transient fields, the SmsInboundRouteBuilderBody.enabled type, and bulkSend now scheduling emails that previously went out …

Findings

Summary

Fixes SDK request and response gaps found by auditing endpoints against the API reference

Request bodies are trimmed so the API no longer sees fields it rejects. Empty lists, rcptTo and the extra sendAt key are dropped from emails, and SMS webhook updates omit events when none are set. This uses transient fields, @Expose(serialize=false) and JsonObject.remove. Response models gain postDeserialize hooks, a shared DateParser and BlocklistedCountAdapter, so wrapped or unwrapped bodies, integer blocklist counts and dates parse. bulkSend() now sends send_at, so scheduled bulk emails stop going out immediately. Bulk status now goes through the normal response handler, so a 404 raises an exception. SMS inbound routes created without .enabled() are now sent without the key, so the API default applies. New tests use CapturingHttpClient to assert the exact URL, method and body sent. VCR fixtures are keyed by a SHA-1 of URL, method and body, so they only match if serialization produces the hashed request.

What's good

  • Deprecated aliases (customnTrackingEnabled, AddRecipient and others) keep the renames source-compatible for existing callers.
  • BlocklistedCountAdapter accepts both boolean and integer blocklisted, and blocklisted is kept as a derived field.
  • CapturingHttpClient lets each fix have a focused regression test on the exact URL, method and JSON body sent, which replayed fixtures cannot show.
Review context · 1 source · 3/5 commands

Reviewed in 4 parts, each by an agent of its own.

Reviews (1) · Last reviewed commit: "MSD-15205: Annotate the deprecated dkim..." · claude-sonnet-5-5

*/
protected void postDeserialize() {

for (InboundRoute route : routes) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 postDeserialize dereferences routes without a null guard · reliability

getRoutes() now calls response.postDeserialize(), which loops over routes. If a 2xx response has no data array, this throws a NullPointerException, where before the call returned the list with routes == null. The sibling SuppressionList, BlocklistListResponse and SmtpUsersList added in this PR all guard against null.

Suggested fix

Add if (routes == null) { return; } at the start of postDeserialize().

Prompt for a coding agent
In src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutesList.java, add a null guard at the start of postDeserialize(): if routes == null, return, before the for loop.

private Date parseDate(String date) {

if (date == null || date.isBlank()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Private parseDate duplicates the new DateParser utility · maintainability

This PR adds com.mailersend.sdk.util.DateParser and moves the recipients classes to it. TemplateTranslation adds a fourth private copy of the same ISO-instant parsing. It also differs in behaviour: it throws on a malformed or space-separated date, while DateParser returns null or parses the space format.

Suggested fix

Replace the four parseDate(...) calls in postDeserialize() with DateParser.parse(...), delete the private parseDate method, and drop the unused java.time imports.

Prompt for a coding agent
In src/main/java/com/mailersend/sdk/templates/TemplateTranslation.java, import com.mailersend.sdk.util.DateParser, replace parseDate(x) calls in postDeserialize() with DateParser.parse(x), delete the private parseDate method and the now-unused java.time imports.

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