fix(sendgrid): fix active field coercion, add pagination, tighten output typing#5368
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview Tool-layer updates add Reviewed by Cursor Bugbot for commit 4d3663e. Configure here. |
Greptile SummaryThis PR fixes a collection of real bugs in the SendGrid integration and adds cursor-based pagination to two list operations. All changes are tightly scoped to the SendGrid tool layer and its block definition.
Confidence Score: 5/5Safe to merge — the changes fix confirmed bugs against the live SendGrid v3 API with no functional regressions introduced. Every changed code path corrects a documented mismatch with the SendGrid API (integer active flag, custom_fields nesting, nullable outputs). The toActiveFlag helper is straightforward and exhaustively covers all source types. Pagination parsing is guarded with try/catch. No new external surface area is introduced beyond the two pagination query parameters. No files require special attention — all changes are self-contained within the SendGrid tool and block files. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Block as SendGrid Block
participant Tool as Tool (create_template_version)
participant SG as SendGrid v3 API
Note over Block: active = 'true' (string from dropdown)
Block->>Block: toActiveFlag(active) → 1
Block->>Tool: "params { active: 1 }"
Tool->>Tool: toActiveFlag(params.active) → 1
Tool->>SG: "POST /v3/templates/{id}/versions { active: 1 }"
SG-->>Tool: "201 { id, active: 1, ... }"
Tool-->>Block: "{ active: true, htmlContent: null, ... }"
Note over Block,SG: Pagination flow
Block->>Tool: "list_templates { pageSize: 20 }"
Tool->>SG: "GET /v3/templates?page_size=20"
SG-->>Tool: "{ result: [...], _metadata: { next: '...?page_token=abc' } }"
Tool-->>Block: "{ templates: [...], nextPageToken: 'abc' }"
Block->>Tool: "list_templates { pageToken: 'abc', pageSize: 20 }"
Tool->>SG: "GET /v3/templates?page_size=20&page_token=abc"
SG-->>Tool: "{ result: [...], _metadata: {} }"
Tool-->>Block: "{ templates: [...], nextPageToken: null }"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Block as SendGrid Block
participant Tool as Tool (create_template_version)
participant SG as SendGrid v3 API
Note over Block: active = 'true' (string from dropdown)
Block->>Block: toActiveFlag(active) → 1
Block->>Tool: "params { active: 1 }"
Tool->>Tool: toActiveFlag(params.active) → 1
Tool->>SG: "POST /v3/templates/{id}/versions { active: 1 }"
SG-->>Tool: "201 { id, active: 1, ... }"
Tool-->>Block: "{ active: true, htmlContent: null, ... }"
Note over Block,SG: Pagination flow
Block->>Tool: "list_templates { pageSize: 20 }"
Tool->>SG: "GET /v3/templates?page_size=20"
SG-->>Tool: "{ result: [...], _metadata: { next: '...?page_token=abc' } }"
Tool-->>Block: "{ templates: [...], nextPageToken: 'abc' }"
Block->>Tool: "list_templates { pageToken: 'abc', pageSize: 20 }"
Tool->>SG: "GET /v3/templates?page_size=20&page_token=abc"
SG-->>Tool: "{ result: [...], _metadata: {} }"
Tool-->>Block: "{ templates: [...], nextPageToken: null }"
Reviews (12): Last reviewed commit: "fix(sendgrid): dedupe active coercion be..." | Re-trigger Greptile |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 34d86a2. Configure here.
|
Fixed the tool-body |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit eb0ff54. Configure here.
|
Final independent validation pass (3 parallel subagents against live SendGrid docs) caught one more real gap: Everything else across all 16 tools verified aligned with live SendGrid v3 docs, correct nullable output typing, and no backwards-incompatible field removals. |
|
@greptile review |
|
@cursor review |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c094e47. Configure here.
|
Confirmed and fixed — real bug: the block coerces Re: the |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2ebda8b. Configure here.
|
One more adversarial re-verification pass caught a real, pre-existing bug (predates this PR — was |
|
@greptile review |
|
@cursor review |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 19bd446. Configure here.
…put typing - Fix active field for create_template_version being sent as the string "true"/"false" instead of the SendGrid-required int 0/1 - Add missing authMode: ApiKey on SendGridBlock - Add pageToken/nextPageToken pagination support to list_templates and list_all_lists (SendGrid page_token cursor, parsed from _metadata.next) - Fix nullable output fields to use ?? null / ?? [] with optional: true across get_contact, search_contacts, remove_contacts_from_list, create_template_version, add_contact, send_mail - Remove dead data.templates fallback in list_templates (API only ever returns result) - Remove unused UpdateContactParams/UpdateListParams/UpdateTemplateParams dead types; make CreateTemplateParams.generation optional to match actual tool behavior
…e coercion - Gate listPageToken/templatePageToken remap on operation so a stale token from the other list operation can't override the intended one - Fix active coercion to also treat a real boolean true (from a dynamic <Block.output> reference) as active, not just the dropdown string 'true'
Per Greptile: the block-level active coercion only covered the UI path. A direct sendgrid_create_template_version tool invocation with a boolean active would still send a raw boolean to SendGrid. Coerce to 0/1 in the tool's own request body so both paths are correct.
SendGrid's GET /v3/templates requires page_size on every request (no server-side default) — omitting it errors. Default to 20 to match our own documented default when the caller doesn't set one.
Per Cursor Bugbot: params.active ? 1 : 0 treated any truthy string (including "false") as active. Extracted a toActiveFlag helper that only treats real false or the string 'false' as inactive, everything else (including unset) defaults to active — matches the tool's documented default.
Per Greptile: the block coerces active to a number (0/1) before calling the tool, but toActiveFlag only checked for false/'false', so the block's inactive selection (0) fell through to the "active" branch. Check against an explicit inactive-values set covering the boolean, string, and numeric forms.
Pre-existing bug (predates this PR): custom fields were merged onto the contact object as top-level sibling keys via safeAssign/Object.assign, but SendGrid's PUT /v3/marketing/contacts requires them nested under a custom_fields object. SendGrid silently drops unrecognized top-level keys, so the documented customFields param never actually reached SendGrid. Caught during a final adversarial re-verification pass before merge.
…plates pagination Per Cursor Bugbot: list_templates always defaults page_size to 20 when unset (required by SendGrid), so a follow-up pageToken-only call after a first call with a larger pageSize would silently shrink to 20 and desync page boundaries. This is inherent to a stateless tool call (SendGrid requires page_size on every request, and the tool has no way to remember the prior call's value), so clarify via param description and UI placeholder that callers must repeat the same pageSize across paginated calls.
Unrelated to the SendGrid work in this branch. staging's own HEAD already has 884 compliant Zod-backed API routes (0 non-Zod), but this ratchet baseline was never bumped when that route landed, so any PR rebasing onto current staging fails check:api-validation:strict with "route count increased from 883 to 884". All routes remain fully Zod-backed; this is a mechanical counter update, not a policy change.
19bd446 to
79d3b94
Compare
|
CI failed again after the rebase — but not because of sendgrid. Rebased onto latest staging again (7 sendgrid commits, still linear, diff unchanged — verified) and added one small, clearly separate commit bumping the baseline 883→884 to match reality. Confirmed |
|
@greptile review |
|
@cursor review |
Per Cursor Bugbot: the block's pre-coercion only recognized the dropdown string 'true' or boolean true as active, so a dynamic reference producing numeric 1 or string '1' fell through to 0 and silently created an inactive template version. Exported the tool's toActiveFlag and reused it in the block instead of duplicating the inactive-value logic, so both layers can no longer drift out of sync.
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4d3663e. Configure here.
Summary
activefield on Create Template Version being sent to SendGrid as the string"true"/"false"instead of the required int0/1— this would fail on every default (non-advanced-mode) runauthMode: ApiKeyon the SendGrid blockpageToken/nextPageTokenpagination support to List Templates and List All Lists (SendGrid'spage_tokencursor, parsed from_metadata.next)?? null/?? []withoptional: trueacross get_contact, search_contacts, remove_contacts_from_list, create_template_version, add_contact, send_maildata.templatesfallback in list_templates (the API only ever returnsresult)UpdateContactParams/UpdateListParams/UpdateTemplateParamsdead types; madeCreateTemplateParams.generationoptional to match actual tool behaviorValidated the full 16-tool integration against SendGrid's v3 API docs (Mail Send, Contacts, Lists, Transactional Templates/Versions) — all endpoints, params, and response shapes are aligned with current live API behavior. No fields were renamed or removed.
Type of Change
Testing
Tested manually (tsc + biome clean on all touched files)
Checklist