Skip to content

Fixes typing to match JS SDK#3

Merged
Huijiro merged 1 commit into
mainfrom
types-fix
Oct 21, 2025
Merged

Fixes typing to match JS SDK#3
Huijiro merged 1 commit into
mainfrom
types-fix

Conversation

@Huijiro

@Huijiro Huijiro commented Oct 21, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Attachment downloads now return HTTP headers alongside file data in a structured response format.
  • Bug Fixes

    • Improved null-safety handling for optional webhook data fields.
    • Enhanced robustness when processing optional email properties.

@coderabbitai

coderabbitai Bot commented Oct 21, 2025

Copy link
Copy Markdown

Walkthrough

The changes restructure the Attachment download API to return a result object containing Data and Headers instead of raw bytes, update multiple webhook-related types to use nullable pointers for improved nil-safety semantics, and add defensive nil-checks to webhook helper methods.

Changes

Cohort / File(s) Summary
Attachment Download API Refactor
inbound.go, attachment_test.go
Modified AttachmentService.Download() to return AttachmentDownloadResponse (containing Data and Headers) instead of raw []byte. Updated test expectations and assertions to validate result structure and HTTP header presence.
Webhook Type Definitions
types.go
Introduced nullable pointer fields across webhook types (WebhookPayload, WebhookEmailData, WebhookAddress, WebhookParsedData, WebhookCleanedContent, WebhookAttachment) to support optional data. Added new public types AttachmentData and AttachmentDownloadResponse. Adjusted import statements to include net/http.
Webhook Nil-Safety Improvements
webhook.go, webhook_test.go
Added nil-checks in GetFromAddress() and GetToAddress() helper methods to safely handle nullable address fields. Updated test assertions to use nil-aware comparisons with detailed error messages for pointer fields.

Sequence Diagram

sequenceDiagram
    participant Client
    participant AttachmentService
    participant HTTP

    rect rgb(200, 220, 240)
    Note over AttachmentService: Old Flow: []byte return
    Client->>AttachmentService: Download(ctx, emailID, filename)
    AttachmentService->>HTTP: GET /attachment
    HTTP-->>AttachmentService: Response + Body
    AttachmentService-->>Client: ([]byte, error)
    end

    rect rgb(240, 220, 200)
    Note over AttachmentService: New Flow: Structured Response
    Client->>AttachmentService: Download(ctx, emailID, filename)
    AttachmentService->>HTTP: GET /attachment
    HTTP-->>AttachmentService: Response + Body
    AttachmentService->>AttachmentService: Extract Data & Headers
    AttachmentService-->>Client: (AttachmentDownloadResponse{Data, Headers}, error)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

The changes span multiple file categories with varying complexity: the attachment API restructuring is straightforward (~15 min), but the extensive webhook type field conversions to nullable pointers require careful semantic review to verify nil-safety and backward compatibility across multiple interdependent types (~30 min), plus webhook helper method nil-checks and test updates (~5–10 min).

Possibly related PRs

Poem

🐰 Hops in with headers held high,
Downloads now bundled, data nearby,
Nil-safe pointers guard the way,
Webhooks whisper, robust and gay!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Fixes typing to match JS SDK" directly relates to the main changes in the pull request. The changeset is primarily focused on type-related modifications across multiple files, including converting fields from non-pointer to pointer types (introducing nullability), changing method return signatures (Download now returns a structured response instead of raw bytes), and introducing new type definitions (AttachmentDownloadResponse, AttachmentData). The title accurately captures that these changes are type-focused fixes aimed at alignment with the JavaScript SDK, and a teammate scanning the commit history would clearly understand the primary purpose without needing additional context.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch types-fix

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

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

@Huijiro Huijiro merged commit 51142fd into main Oct 21, 2025
8 of 9 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
attachment_test.go (1)

124-134: Good validations on data and headers; add a couple more assertions

  • Also assert Content-Disposition matches filename.
  • Assert server received no Content-Type header for GET download to enforce the contract.

Example additions inside the httptest handler and assertions:

// inside handler, before sending response:
if ct := r.Header.Get("Content-Type"); ct != "" {
    t.Errorf("Expected no Content-Type on GET download, got '%s'", ct)
}

// after result.Headers checks:
if cd := result.Headers.Get("Content-Disposition"); cd == "" || !strings.Contains(cd, tt.filename) {
    t.Errorf("Expected Content-Disposition to include filename '%s', got '%s'", tt.filename, cd)
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5b2457 and be77490.

📒 Files selected for processing (5)
  • attachment_test.go (3 hunks)
  • inbound.go (4 hunks)
  • types.go (5 hunks)
  • webhook.go (1 hunks)
  • webhook_test.go (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use PascalCase for exported types, methods, and functions; use camelCase for unexported identifiers
All exported functions must have GoDoc comments
Keep comments concise and focused on usage, not implementation details

Files:

  • inbound.go
  • webhook_test.go
  • webhook.go
  • types.go
  • attachment_test.go
inbound.go

📄 CodeRabbit inference engine (AGENTS.md)

inbound.go: Include an API Reference: link in GoDoc comments for exported API methods where applicable
Return Go errors for network/client failures; return API errors via ApiResponse.Error
Use fmt.Errorf with %w for error wrapping
For direct data returns (e.g., attachment Download), return standard Go errors (not ApiResponse)
Provide pointer helper functions: String, Int, Bool at the bottom of the file
When adding a new API method, implement the method on the appropriate service in inbound.go
When adding a new API method, include a GoDoc comment with an API reference link
When adding a new service, define the service struct holding a *Inbound client reference
When adding a new service, add a constructor: func NewX(client *Inbound) *X
When adding a new service, add an accessor on Inbound: func (c *Inbound) X() *X
Implement service methods using the generic makeRequest[T] helper
All API methods should return *ApiResponse[T] for typed responses
Use buildQueryString() to construct GET query parameters from request structs
Escape URL path parameters with url.PathEscape()
HTTP client default timeout is 30s; allow customization via WithHTTPClient()
Include Authorization: Bearer header on all requests
Set Content-Type: application/json on all requests except attachment downloads
All API methods accept context.Context as the first parameter and use http.NewRequestWithContext
Respect context timeouts and cancellations in request execution
Support idempotency for send, reply, and schedule operations using IdempotencyOptions and the Idempotency-Key header
Follow the provided method template: take ctx, build endpoint (with buildQueryString for GET), and call makeRequest[T]
For optional headers (e.g., Idempotency-Key), build a headers map conditionally and pass to makeRequest

Files:

  • inbound.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Organize tests as one test file per service (e.g., send_email_test.go, webhook_test.go)
Use httptest.NewServer to mock API responses in tests
Test both success and error cases for service methods
Verify HTTP method, Authorization header, and request body in tests
Structure tests as table-driven tests with t.Run subtests

Files:

  • webhook_test.go
  • attachment_test.go
types.go

📄 CodeRabbit inference engine (AGENTS.md)

types.go: Prefix request types with the HTTP method (e.g., PostEmailsRequest, GetMailRequest)
Suffix response types with Response (e.g., PostEmailsResponse)
Use pointers for optional fields (e.g., *string, *int, *bool)
Use type any for fields that can accept multiple types (e.g., string or []string)
Add JSON tags to all struct fields
Use omitempty for optional request fields in JSON tags
When adding a new API method, define request/response types in types.go
Ensure query parameters are driven by struct fields with json tags

Files:

  • types.go
🧠 Learnings (1)
📚 Learning: 2025-10-09T20:15:43.730Z
Learnt from: CR
PR: inboundemail/inbound-golang-sdk#0
File: AGENTS.md:0-0
Timestamp: 2025-10-09T20:15:43.730Z
Learning: Applies to inbound.go : Provide pointer helper functions: String, Int, Bool at the bottom of the file

Applied to files:

  • inbound.go
🧬 Code graph analysis (2)
inbound.go (1)
types.go (1)
  • AttachmentDownloadResponse (803-806)
webhook.go (1)
types.go (1)
  • WebhookPayload (728-733)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (1.22.x, windows-latest)
  • GitHub Check: test (1.21.x, windows-latest)
  • GitHub Check: test (1.21.x, macos-latest)
🔇 Additional comments (20)
inbound.go (3)

303-323: Docs/idempotency for Send look good

API reference link present; conditional Idempotency-Key header applied only when provided.


347-359: Schedule docs and header handling LGTM

Clear usage notes and proper optional idempotency header wiring.


754-767: Pointer helpers provided as required

String/Int/Bool helpers present at file bottom.

Based on learnings

attachment_test.go (1)

106-107: Test update to new return type looks correct

Storing result and checking errors aligns with new signature.

types.go (9)

3-6: Imports for http/time are appropriate

Needed for http.Header in AttachmentDownloadResponse and time fields elsewhere.


589-599: ThreadSummary fields and tags consistent

Matches existing naming conventions; optional LatestMessage pointer makes sense.


626-631: ThreadAttachment required fields OK

Keeping these non-pointer aligns with thread retrieval guarantees.


729-745: WebhookPayload/WebhookEmailData pointer semantics align with nil-safety

Event/timestamp remain required; From/To/Subject as pointers is consistent with incoming variability.


753-755: *WebhookAddress.Address as string prevents nil deref issues

Matches helper changes in webhook.go.


758-774: ParsedData optional fields moved to pointers; good

Headers stays map[string]any; Priority as any covers string|false cases.


776-783: CleanedContent text/html as pointers is reasonable

Keeps parity with parser variability.


785-792: WebhookAttachment pointer fields + non-pointer DownloadUrl

This mirrors JS SDK semantics; looks good.


802-806: AttachmentDownloadResponse shape looks right

Data []byte + Headers http.Header matches new Download API.

webhook.go (2)

22-31: Nil-safety and formatting in GetFromAddress are solid

Defensive checks on From and Address pointers prevent panics; formatted "Name " output is correct.


37-46: Mirrored improvements for GetToAddress LGTM

Same robust handling for To; consistent behavior.

webhook_test.go (5)

114-129: Nil-aware checks for MessageID/Subject are correct

Clear error messages when nil vs wrong value.


146-160: ParsedData text/html assertions properly handle pointers

Good split between presence and content checks.


163-177: CleanedContent text/html pointer checks LGTM

Matches updated types.


179-185: HasHTML/HasText boolean checks preserved

Straightforward and correct.


203-213: Endpoint field assertions intact

IDs and types verified as expected.

Comment thread inbound.go
Comment on lines +628 to +649
func (s *AttachmentService) Download(ctx context.Context, emailID, filename string) (*AttachmentDownloadResponse, error) {
endpoint := fmt.Sprintf("/attachments/%s/%s", emailID, url.PathEscape(filename))

resp, err := s.client.request(ctx, "GET", endpoint, nil, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()

data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

if resp.StatusCode >= 400 {
respBody, _ := io.ReadAll(resp.Body)
var errorResp struct {
Error string `json:"error"`
}
if json.Unmarshal(respBody, &errorResp) == nil && errorResp.Error != "" {
return nil, fmt.Errorf("%s", errorResp.Error)
}
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status)
}

return io.ReadAll(resp.Body)
return &AttachmentDownloadResponse{
Data: data,
Headers: resp.Header,
}, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Attachment download: escape all path params, avoid JSON Content-Type on binary GET, and check status before reading body

  • Escape emailID too to prevent path issues.
  • request() currently sets Content-Type: application/json on all requests; downloads should not send it.
  • Return early on non-2xx before reading the body; also drain body on error for connection reuse.

Apply this diff inside Download:

- endpoint := fmt.Sprintf("/attachments/%s/%s", emailID, url.PathEscape(filename))
+ endpoint := fmt.Sprintf("/attachments/%s/%s", url.PathEscape(emailID), url.PathEscape(filename))
@@
- data, err := io.ReadAll(resp.Body)
- if err != nil {
-   return nil, err
- }
-
- if resp.StatusCode >= 400 {
-   return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status)
- }
+ if resp.StatusCode >= 400 {
+   // drain to allow connection reuse
+   _, _ = io.Copy(io.Discard, resp.Body)
+   return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status)
+ }
+ data, err := io.ReadAll(resp.Body)
+ if err != nil {
+   return nil, err
+ }

Additionally, update request() to set Content-Type only when sending JSON:

// in func (c *Inbound) request(...)
req.Header.Set("Authorization", "Bearer "+c.apiKey)
// Set JSON Content-Type only when we actually have a JSON body
if body != nil {
    req.Header.Set("Content-Type", "application/json")
}

If you prefer not to touch request(), pass a header override in Download and then delete it before send; but adjusting request() is cleaner and aligns with “Set Content-Type: application/json on all requests except attachment downloads”. As per coding guidelines.

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