Documentation
¶
Overview ¶
Package gmail provides a Gmail API client with rate limiting and retry logic.
Index ¶
- Constants
- type API
- type AccountReader
- type Client
- func (c *Client) BatchDeleteMessages(ctx context.Context, messageIDs []string) error
- func (c *Client) Close() error
- func (c *Client) DeleteMessage(ctx context.Context, messageID string) error
- func (c *Client) GetMessageRaw(ctx context.Context, messageID string) (*RawMessage, error)
- func (c *Client) GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
- func (c *Client) GetProfile(ctx context.Context) (*Profile, error)
- func (c *Client) ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
- func (c *Client) ListLabels(ctx context.Context) ([]*Label, error)
- func (c *Client) ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
- func (c *Client) TrashMessage(ctx context.Context, messageID string) error
- type ClientOption
- type Clock
- type DeletionCall
- type DeletionMockAPI
- func (m *DeletionMockAPI) BatchDeleteMessages(ctx context.Context, messageIDs []string) error
- func (m *DeletionMockAPI) Close() error
- func (m *DeletionMockAPI) DeleteMessage(ctx context.Context, messageID string) error
- func (m *DeletionMockAPI) GetDeleteCallCount(messageID string) int
- func (m *DeletionMockAPI) GetMessageRaw(ctx context.Context, messageID string) (*RawMessage, error)
- func (m *DeletionMockAPI) GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
- func (m *DeletionMockAPI) GetProfile(ctx context.Context) (*Profile, error)
- func (m *DeletionMockAPI) GetTrashCallCount(messageID string) int
- func (m *DeletionMockAPI) ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
- func (m *DeletionMockAPI) ListLabels(ctx context.Context) ([]*Label, error)
- func (m *DeletionMockAPI) ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
- func (m *DeletionMockAPI) Reset()
- func (m *DeletionMockAPI) SetNotFoundError(messageID string)
- func (m *DeletionMockAPI) SetTransientFailure(messageID string, failCount int, forTrash bool)
- func (m *DeletionMockAPI) TrashMessage(ctx context.Context, messageID string) error
- type HistoryLabelChange
- type HistoryMessage
- type HistoryRecord
- type HistoryResponse
- type Label
- type MessageDeleter
- type MessageID
- type MessageListResponse
- type MessageReader
- type MockAPI
- func (m *MockAPI) AddMessage(id string, raw []byte, labelIDs []string)
- func (m *MockAPI) BatchDeleteMessages(ctx context.Context, messageIDs []string) error
- func (m *MockAPI) Close() error
- func (m *MockAPI) DeleteMessage(ctx context.Context, messageID string) error
- func (m *MockAPI) GetMessageRaw(ctx context.Context, messageID string) (*RawMessage, error)
- func (m *MockAPI) GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
- func (m *MockAPI) GetProfile(ctx context.Context) (*Profile, error)
- func (m *MockAPI) ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
- func (m *MockAPI) ListLabels(ctx context.Context) ([]*Label, error)
- func (m *MockAPI) ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
- func (m *MockAPI) Reset()
- func (m *MockAPI) SetupMessages(msgs ...*RawMessage)
- func (m *MockAPI) TrashMessage(ctx context.Context, messageID string) error
- type NotFoundError
- type NullProgress
- type Operation
- type Profile
- type RateLimitError
- type RateLimiter
- type RawMessage
- type SyncProgress
- type SyncProgressWithDate
- type SyncSummary
Constants ¶
const ( OpTrash = "trash" OpDelete = "delete" OpBatchDelete = "batch_delete" )
Operation constants for call tracking.
const DefaultCapacity = 250
DefaultCapacity is the default token bucket capacity (Gmail's per-user quota).
const DefaultRefillRate = 250.0
DefaultRefillRate is tokens per second at the default rate.
const MinQPS = 0.1
MinQPS is the minimum allowed QPS to prevent division by zero.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type API ¶
type API interface {
AccountReader
MessageReader
MessageDeleter
// Close releases any resources held by the client.
Close() error
}
API defines the interface for Gmail operations. This interface enables mocking for tests without hitting the real API.
type AccountReader ¶ added in v0.4.0
type AccountReader interface {
// GetProfile returns the authenticated user's profile.
GetProfile(ctx context.Context) (*Profile, error)
// ListLabels returns all labels for the account.
ListLabels(ctx context.Context) ([]*Label, error)
}
AccountReader provides read access to account-level Gmail data.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client implements the Gmail API interface.
func NewClient ¶
func NewClient(tokenSource oauth2.TokenSource, opts ...ClientOption) *Client
NewClient creates a new Gmail API client.
func (*Client) BatchDeleteMessages ¶
BatchDeleteMessages permanently deletes multiple messages.
func (*Client) DeleteMessage ¶
DeleteMessage permanently deletes a message.
func (*Client) GetMessageRaw ¶
GetMessageRaw fetches a single message with raw MIME data.
func (*Client) GetMessagesRawBatch ¶
func (c *Client) GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
GetMessagesRawBatch fetches multiple messages in parallel with rate limiting.
func (*Client) GetProfile ¶
GetProfile returns the authenticated user's profile.
func (*Client) ListHistory ¶
func (c *Client) ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
ListHistory returns changes since the given history ID.
func (*Client) ListLabels ¶
ListLabels returns all labels for the account.
func (*Client) ListMessages ¶
func (c *Client) ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
ListMessages returns message IDs matching the query.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client.
func WithConcurrency ¶
func WithConcurrency(n int) ClientOption
WithConcurrency sets the max concurrent requests for batch operations.
func WithLogger ¶
func WithLogger(logger *slog.Logger) ClientOption
WithLogger sets the logger for the client.
func WithRateLimiter ¶
func WithRateLimiter(rl *RateLimiter) ClientOption
WithRateLimiter sets a custom rate limiter.
type DeletionCall ¶
type DeletionCall struct {
Operation string // OpTrash, OpDelete, or OpBatchDelete
MessageID string // For single operations
BatchIDs []string // For batch operations
Error error // Error returned (nil for success)
}
DeletionCall represents a single API call for sequence tracking.
type DeletionMockAPI ¶
type DeletionMockAPI struct {
// Per-message error injection for Trash operations
// Key: messageID, Value: error to return (or nil for success)
TrashErrors map[string]error
// Per-message error injection for Delete operations
DeleteErrors map[string]error
// Batch delete error - returned when BatchDeleteMessages is called
BatchDeleteError error
// Transient errors - fail N times then succeed
// Key: messageID, Value: number of failures remaining
TransientTrashFailures map[string]int
TransientDeleteFailures map[string]int
// Rate limit simulation
RateLimitAfterCalls int // Trigger rate limit after this many calls (0 = disabled)
RateLimitDuration int // Seconds to suggest retry (for 429 Retry-After)
// Call tracking
TrashCalls []string // Message IDs passed to TrashMessage
DeleteCalls []string // Message IDs passed to DeleteMessage
BatchDeleteCalls [][]string // Batches passed to BatchDeleteMessages
// Call sequence tracking (for verifying retry behavior)
CallSequence []DeletionCall
// Hooks for custom behavior
BeforeTrash func(messageID string) error // Called before processing trash
BeforeDelete func(messageID string) error // Called before processing delete
BeforeBatchDelete func(messageIDs []string) error
// contains filtered or unexported fields
}
DeletionMockAPI is a mock Gmail API specifically designed for testing deletion scenarios with comprehensive error injection and call tracking.
func NewDeletionMockAPI ¶
func NewDeletionMockAPI() *DeletionMockAPI
NewDeletionMockAPI creates a new deletion mock with empty state.
func (*DeletionMockAPI) BatchDeleteMessages ¶
func (m *DeletionMockAPI) BatchDeleteMessages(ctx context.Context, messageIDs []string) error
BatchDeleteMessages simulates batch deletion with error injection.
func (*DeletionMockAPI) Close ¶
func (m *DeletionMockAPI) Close() error
func (*DeletionMockAPI) DeleteMessage ¶
func (m *DeletionMockAPI) DeleteMessage(ctx context.Context, messageID string) error
DeleteMessage simulates permanently deleting a message with error injection.
func (*DeletionMockAPI) GetDeleteCallCount ¶
func (m *DeletionMockAPI) GetDeleteCallCount(messageID string) int
GetDeleteCallCount returns the number of delete calls for a message.
func (*DeletionMockAPI) GetMessageRaw ¶
func (m *DeletionMockAPI) GetMessageRaw(ctx context.Context, messageID string) (*RawMessage, error)
func (*DeletionMockAPI) GetMessagesRawBatch ¶
func (m *DeletionMockAPI) GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
func (*DeletionMockAPI) GetProfile ¶
func (m *DeletionMockAPI) GetProfile(ctx context.Context) (*Profile, error)
func (*DeletionMockAPI) GetTrashCallCount ¶
func (m *DeletionMockAPI) GetTrashCallCount(messageID string) int
GetTrashCallCount returns the number of trash calls for a message.
func (*DeletionMockAPI) ListHistory ¶
func (m *DeletionMockAPI) ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
func (*DeletionMockAPI) ListLabels ¶
func (m *DeletionMockAPI) ListLabels(ctx context.Context) ([]*Label, error)
func (*DeletionMockAPI) ListMessages ¶
func (m *DeletionMockAPI) ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
func (*DeletionMockAPI) Reset ¶
func (m *DeletionMockAPI) Reset()
Reset clears all state and call tracking.
func (*DeletionMockAPI) SetNotFoundError ¶
func (m *DeletionMockAPI) SetNotFoundError(messageID string)
SetNotFoundError sets a 404 error for a message ID.
func (*DeletionMockAPI) SetTransientFailure ¶
func (m *DeletionMockAPI) SetTransientFailure(messageID string, failCount int, forTrash bool)
SetTransientFailure sets a message to fail N times then succeed.
func (*DeletionMockAPI) TrashMessage ¶
func (m *DeletionMockAPI) TrashMessage(ctx context.Context, messageID string) error
TrashMessage simulates trashing a message with error injection.
type HistoryLabelChange ¶
HistoryLabelChange represents a label change in history.
type HistoryMessage ¶
type HistoryMessage struct {
Message MessageID
}
HistoryMessage represents a message in history.
type HistoryRecord ¶
type HistoryRecord struct {
ID uint64
MessagesAdded []HistoryMessage
MessagesDeleted []HistoryMessage
LabelsAdded []HistoryLabelChange
LabelsRemoved []HistoryLabelChange
}
HistoryRecord represents a single history change.
type HistoryResponse ¶
type HistoryResponse struct {
History []HistoryRecord
NextPageToken string
HistoryID uint64
}
HistoryResponse contains changes since a history ID.
type Label ¶
type Label struct {
ID string
Name string
Type string // "system" or "user"
MessagesTotal int64
MessagesUnread int64
MessageListVisibility string
LabelListVisibility string
}
Label represents a Gmail label.
type MessageDeleter ¶ added in v0.4.0
type MessageDeleter interface {
// TrashMessage moves a message to trash (recoverable for 30 days).
TrashMessage(ctx context.Context, messageID string) error
// DeleteMessage permanently deletes a message.
DeleteMessage(ctx context.Context, messageID string) error
// BatchDeleteMessages permanently deletes multiple messages (max 1000).
BatchDeleteMessages(ctx context.Context, messageIDs []string) error
}
MessageDeleter provides write operations for deleting Gmail messages.
type MessageListResponse ¶
type MessageListResponse struct {
Messages []MessageID
NextPageToken string
ResultSizeEstimate int64
}
MessageListResponse contains a page of message IDs.
type MessageReader ¶ added in v0.4.0
type MessageReader interface {
// ListMessages returns message IDs matching the query.
// Use pageToken for pagination. Returns next page token if more results exist.
ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
// GetMessageRaw fetches a single message with raw MIME data.
GetMessageRaw(ctx context.Context, messageID string) (*RawMessage, error)
// GetMessagesRawBatch fetches multiple messages in parallel with rate limiting.
// Returns results in the same order as input IDs. Failed fetches return nil.
GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
// ListHistory returns changes since the given history ID.
ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
}
MessageReader provides read access to Gmail messages and history.
type MockAPI ¶
type MockAPI struct {
// Profile to return
Profile *Profile
// Labels to return
Labels []*Label
// Messages indexed by ID
Messages map[string]*RawMessage
// Message list pages - each page is a list of message IDs
MessagePages [][]string
// History records
HistoryRecords []HistoryRecord
HistoryID uint64
// UseRawThreadID uses the ThreadID from RawMessage instead of generating "thread_" + id
UseRawThreadID bool
// ListThreadIDOverride overrides the threadID returned by ListMessages for specific message IDs.
// This allows testing the case where list returns empty threadID but raw message has one.
ListThreadIDOverride map[string]string
// Error injection
ProfileError error
LabelsError error
ListMessagesError error
GetMessageError map[string]error // Per-message errors
HistoryError error
// Call tracking for assertions
ProfileCalls int
LabelsCalls int
ListMessagesCalls int
LastQuery string // Last query passed to ListMessages
GetMessageCalls []string
HistoryCalls []uint64
TrashCalls []string
DeleteCalls []string
BatchDeleteCalls [][]string
// contains filtered or unexported fields
}
MockAPI is a mock implementation of the Gmail API for testing.
func (*MockAPI) AddMessage ¶
AddMessage adds a message to the mock store.
func (*MockAPI) BatchDeleteMessages ¶
BatchDeleteMessages records a batch delete call.
func (*MockAPI) DeleteMessage ¶
DeleteMessage records a delete call.
func (*MockAPI) GetMessageRaw ¶
GetMessageRaw returns a mock message.
func (*MockAPI) GetMessagesRawBatch ¶
func (m *MockAPI) GetMessagesRawBatch(ctx context.Context, messageIDs []string) ([]*RawMessage, error)
GetMessagesRawBatch fetches multiple messages. Mirrors the real Client behavior: individual fetch errors leave a nil entry in the results slice rather than failing the entire batch. Callers must handle nil entries (see sync.go).
func (*MockAPI) GetProfile ¶
GetProfile returns the mock profile.
func (*MockAPI) ListHistory ¶
func (m *MockAPI) ListHistory(ctx context.Context, startHistoryID uint64, pageToken string) (*HistoryResponse, error)
ListHistory returns mock history records.
func (*MockAPI) ListLabels ¶
ListLabels returns the mock labels.
func (*MockAPI) ListMessages ¶
func (m *MockAPI) ListMessages(ctx context.Context, query string, pageToken string) (*MessageListResponse, error)
ListMessages returns mock message IDs with pagination.
func (*MockAPI) SetupMessages ¶ added in v0.4.0
func (m *MockAPI) SetupMessages(msgs ...*RawMessage)
SetupMessages adds multiple pre-built RawMessage values to the mock store in a thread-safe manner. Nil entries in the input slice are silently skipped.
type NotFoundError ¶
type NotFoundError struct {
Path string
}
NotFoundError indicates a 404 response.
func (*NotFoundError) Error ¶
func (e *NotFoundError) Error() string
type NullProgress ¶
type NullProgress struct{}
NullProgress is a no-op progress reporter.
func (NullProgress) OnComplete ¶
func (NullProgress) OnComplete(summary *SyncSummary)
func (NullProgress) OnError ¶
func (NullProgress) OnError(err error)
func (NullProgress) OnLatestDate ¶
func (NullProgress) OnLatestDate(date time.Time)
func (NullProgress) OnProgress ¶
func (NullProgress) OnProgress(processed, added, skipped int64)
func (NullProgress) OnStart ¶
func (NullProgress) OnStart(total int64)
type RateLimitError ¶
type RateLimitError struct {
RetryAfter int // Seconds to wait before retry
}
RateLimitError represents a rate limit response.
func (*RateLimitError) Error ¶
func (e *RateLimitError) Error() string
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter implements a token bucket rate limiter for Gmail API calls. It is safe for concurrent use and supports adaptive throttling.
func NewRateLimiter ¶
func NewRateLimiter(qps float64) *RateLimiter
NewRateLimiter creates a rate limiter with the specified QPS. A qps of 5 is the default safe rate for Gmail API. QPS is clamped to a minimum of MinQPS (0.1) to prevent division by zero.
func (*RateLimiter) Acquire ¶
func (r *RateLimiter) Acquire(ctx context.Context, op Operation) error
Acquire blocks until the required tokens are available. Returns an error if the context is cancelled.
func (*RateLimiter) Available ¶
func (r *RateLimiter) Available() float64
Available returns the current number of available tokens.
func (*RateLimiter) RecoverRate ¶
func (r *RateLimiter) RecoverRate()
RecoverRate restores the original refill rate after throttling.
func (*RateLimiter) Throttle ¶
func (r *RateLimiter) Throttle(duration time.Duration)
Throttle temporarily reduces the rate when we hit API rate limits. This provides adaptive back-pressure when Gmail returns 429/403 quota errors.
func (*RateLimiter) TryAcquire ¶
func (r *RateLimiter) TryAcquire(op Operation) bool
TryAcquire attempts to acquire tokens without blocking. Returns true if successful, false if insufficient tokens.
type RawMessage ¶
type RawMessage struct {
ID string
ThreadID string
LabelIDs []string
Snippet string
HistoryID uint64
InternalDate int64 // Unix milliseconds
SizeEstimate int64
Raw []byte // Decoded from base64url
}
RawMessage contains the raw MIME data for a message.
type SyncProgress ¶
type SyncProgress interface {
// OnStart is called when sync begins.
OnStart(total int64)
// OnProgress is called periodically during sync.
OnProgress(processed, added, skipped int64)
// OnComplete is called when sync finishes.
OnComplete(summary *SyncSummary)
// OnError is called when an error occurs.
OnError(err error)
}
SyncProgress reports sync progress to the caller.
type SyncProgressWithDate ¶
type SyncProgressWithDate interface {
SyncProgress
// OnLatestDate reports the date of the most recently processed message.
// This helps show where in the mailbox the sync is currently processing.
OnLatestDate(date time.Time)
}
SyncProgressWithDate is an optional extension of SyncProgress that provides message date info for better progress context.
type SyncSummary ¶
type SyncSummary struct {
StartTime time.Time
EndTime time.Time
Duration time.Duration
MessagesFound int64
MessagesAdded int64
MessagesUpdated int64
MessagesSkipped int64
BytesDownloaded int64
Errors int64
FinalHistoryID uint64
WasResumed bool
ResumedFromToken string
}
SyncSummary contains statistics about a completed sync.