Guide for implementing new Nylas API adapters.
Adapters implement the ports.NylasClient interface and handle HTTP communication with the Nylas API.
Location: internal/adapters/nylas/
Add methods to internal/ports/nylas.go:
type NylasClient interface {
// ... existing methods
// New feature methods
GetResource(ctx context.Context, id string) (*domain.Resource, error)
ListResources(ctx context.Context, params *domain.ResourceParams) ([]*domain.Resource, error)
}Create internal/adapters/nylas/resource.go:
package nylas
import (
"context"
"fmt"
"net/http"
)
func (c *Client) GetResource(ctx context.Context, id string) (*domain.Resource, error) {
url := fmt.Sprintf("%s/v3/resources/%s", c.baseURL, id)
req, err := c.newRequest(ctx, http.MethodGet, url, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
var resource domain.Resource
if err := c.do(req, &resource); err != nil {
return nil, fmt.Errorf("failed to get resource: %w", err)
}
return &resource, nil
}Update internal/adapters/nylas/mock.go:
type MockClient struct {
GetResourceFunc func(ctx context.Context, id string) (*domain.Resource, error)
}
func (m *MockClient) GetResource(ctx context.Context, id string) (*domain.Resource, error) {
if m.GetResourceFunc != nil {
return m.GetResourceFunc(ctx, id)
}
return nil, nil
}func TestGetResource(t *testing.T) {
mock := &MockClient{
GetResourceFunc: func(ctx context.Context, id string) (*domain.Resource, error) {
return &domain.Resource{ID: id}, nil
},
}
resource, err := mock.GetResource(context.Background(), "test-id")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resource.ID != "test-id" {
t.Errorf("expected ID 'test-id', got '%s'", resource.ID)
}
}func (c *Client) Get(ctx context.Context, id string) (*domain.Object, error) {
url := fmt.Sprintf("%s/v3/objects/%s", c.baseURL, id)
req, err := c.newRequest(ctx, http.MethodGet, url, nil, nil)
if err != nil {
return nil, err
}
var obj domain.Object
return &obj, c.do(req, &obj)
}func (c *Client) Create(ctx context.Context, obj *domain.Object) (*domain.Object, error) {
url := fmt.Sprintf("%s/v3/objects", c.baseURL)
req, err := c.newRequest(ctx, http.MethodPost, url, nil, obj)
if err != nil {
return nil, err
}
var created domain.Object
return &created, c.do(req, &created)
}func (c *Client) List(ctx context.Context, params *domain.ListParams) ([]*domain.Object, error) {
url := fmt.Sprintf("%s/v3/objects", c.baseURL)
query := make(map[string]string)
if params.Limit > 0 {
query["limit"] = fmt.Sprintf("%d", params.Limit)
}
if params.Offset > 0 {
query["offset"] = fmt.Sprintf("%d", params.Offset)
}
req, err := c.newRequest(ctx, http.MethodGet, url, query, nil)
if err != nil {
return nil, err
}
var response struct {
Data []*domain.Object `json:"data"`
}
return response.Data, c.do(req, &response)
}// Wrap all errors with context
if err != nil {
return nil, fmt.Errorf("failed to <operation>: %w", err)
}
// Check HTTP status codes
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}- Unit tests - Test with mocks
- Integration tests - Test with real API
- Error cases - Test failure paths
- Edge cases - Empty responses, large datasets
- Port Interface:
internal/ports/nylas.go - Existing Adapters:
internal/adapters/nylas/ - Testing Guide: testing-guide.md