Skip to content

feat: Add Audiences, Contacts, and Broadcasts API support#7

Draft
themusicman wants to merge 4 commits into
elixir-saas:mainfrom
themusicman:expand-api-support
Draft

feat: Add Audiences, Contacts, and Broadcasts API support#7
themusicman wants to merge 4 commits into
elixir-saas:mainfrom
themusicman:expand-api-support

Conversation

@themusicman

Copy link
Copy Markdown

This PR expands the Resend Elixir client by adding support for three new API endpoints:

🎯 New Features

Audiences Module

  • `Resend.Audiences`: Complete CRUD operations for audience management
  • Create, retrieve, update, and delete audiences
  • List all audiences with pagination support
  • Comprehensive test coverage

Contacts Module

  • `Resend.Contacts`: Full contact management within audiences
  • Add, retrieve, update, and remove contacts from audiences
  • List contacts with filtering and pagination
  • Robust error handling and validation

Broadcasts Module

  • `Resend.Broadcasts`: Email campaign management
  • Create and send email broadcasts to audiences
  • Retrieve broadcast details and status
  • List all broadcasts with pagination

🔧 Technical Improvements

  • Updated `Resend.Client` to support new API endpoints
  • Added proper structs for all new data types
  • Comprehensive test suites for all new modules
  • Consistent error handling across all new features
  • Updated version in `mix.exs`

📋 Changes Summary

  • `lib/resend/audiences.ex` - New audiences management module
  • `lib/resend/audiences/audience.ex` - Audience struct definition
  • `lib/resend/contacts.ex` - New contacts management module
  • `lib/resend/contacts/contact.ex` - Contact struct definition
  • `lib/resend/broadcasts.ex` - New broadcasts management module
  • `lib/resend/broadcasts/broadcast.ex` - Broadcast struct definition
  • `lib/resend/client.ex` - Updated to support new endpoints
  • `mix.exs` - Version bump
  • Comprehensive test coverage for all new modules

🧪 Testing

All new modules include comprehensive test suites with:

  • Success case testing
  • Error handling validation
  • Mock API responses
  • Edge case coverage

This expands the Resend Elixir client to support the full Resend API surface area for audience and campaign management.

- Introduced `Resend.Audiences` module with functions to create, list, retrieve, and delete audiences.
- Added `Resend.Audiences.Audience` struct to represent audience data.
- Updated `mix.exs` to include new modules in the project.
- Implemented comprehensive tests for audience management functionalities.
- Added `Resend.Contacts` module with functions to create, list, retrieve, update, and delete contacts within audiences.
- Introduced `Resend.Contacts.Contact` struct to represent individual contact data.
- Updated `mix.exs` to include the new Contacts module.
- Implemented comprehensive tests for contact management functionalities.
- Introduced `Resend.Broadcasts` module with functions to create, list, retrieve, update, send, and delete broadcasts.
- Added `Resend.Broadcasts.Broadcast` struct to represent broadcast data.
- Updated `mix.exs` to include the new Broadcasts module.
- Implemented comprehensive tests for broadcast management functionalities.
- Removed redundant fields from success body assertions in `broadcasts_test.exs`.
- Updated assertions to focus on the `id` field, enhancing clarity and maintainability of tests.
Copilot AI review requested due to automatic review settings July 16, 2025 11:40

Copilot AI 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.

Pull Request Overview

This PR significantly expands the Resend Elixir client by adding support for three new API endpoint categories: Audiences, Contacts, and Broadcasts. The purpose is to provide complete CRUD operations for audience management and email campaign functionality.

Key Changes

  • Added complete Audiences module for creating and managing email audience groups
  • Added Contacts module for managing individual contacts within audiences
  • Added Broadcasts module for creating and sending email campaigns to audiences
  • Updated the Client module to support PATCH HTTP method
  • Added comprehensive test coverage for all new functionality

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/resend/audiences.ex New module providing CRUD operations for audience management
lib/resend/audiences/audience.ex Audience struct definition with casting functionality
lib/resend/contacts.ex New module for managing contacts within audiences
lib/resend/contacts/contact.ex Contact struct definition with casting functionality
lib/resend/broadcasts.ex New module for email campaign management
lib/resend/broadcasts/broadcast.ex Broadcast struct definition with casting functionality
lib/resend/client.ex Added PATCH method support for update operations
mix.exs Updated documentation structure to include new modules
test/resend/audiences_test.exs Comprehensive test suite for audiences functionality
test/resend/audiences/audience_test.exs Unit tests for audience struct casting
test/resend/contacts_test.exs Comprehensive test suite for contacts functionality
test/resend/contacts/contact_test.exs Unit tests for contact struct casting
test/resend/broadcasts_test.exs Comprehensive test suite for broadcasts functionality
test/resend/broadcasts/broadcast_test.exs Unit tests for broadcast struct casting

# When called with 2 args, the second arg is interpreted as broadcast_id
# So we need to use the default client and pass broadcast_id and opts
assert {:ok, %Broadcast{id: ^broadcast_id}} =
Resend.Broadcasts.send(Resend.client(), broadcast_id, scheduled_at: scheduled_at)

Copilot AI Jul 16, 2025

Copy link

Choose a reason for hiding this comment

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

The function call explicitly passes Resend.client() which is redundant since the send/3 function already defaults to Resend.client(). This should be simplified to Resend.Broadcasts.send(broadcast_id, scheduled_at: scheduled_at) for consistency with the test on line 375 in the same file.

Suggested change
Resend.Broadcasts.send(Resend.client(), broadcast_id, scheduled_at: scheduled_at)
Resend.Broadcasts.send(broadcast_id, scheduled_at: scheduled_at)

Copilot uses AI. Check for mistakes.

# Fix: Same issue here, need to use proper argument order
assert {:ok, %Broadcast{id: ^broadcast_id}} =
Resend.Broadcasts.send(Resend.client(), broadcast_id, scheduled_at: nil)

Copilot AI Jul 16, 2025

Copy link

Choose a reason for hiding this comment

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

Same issue as line 379 - the explicit Resend.client() call is redundant and inconsistent with other test patterns. Should be simplified to Resend.Broadcasts.send(broadcast_id, scheduled_at: nil).

Suggested change
Resend.Broadcasts.send(Resend.client(), broadcast_id, scheduled_at: nil)
Resend.Broadcasts.send(broadcast_id, scheduled_at: nil)

Copilot uses AI. Check for mistakes.
Comment thread lib/resend/broadcasts.ex
Comment on lines +238 to +240
case Keyword.has_key?(opts, :scheduled_at) do
true -> %{scheduled_at: opts[:scheduled_at]}
false -> %{}

Copilot AI Jul 16, 2025

Copy link

Choose a reason for hiding this comment

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

The send function's body construction logic should handle DateTime serialization consistently. When scheduled_at is a DateTime, it should be converted to ISO8601 format before being included in the request body to ensure proper API communication.

Suggested change
case Keyword.has_key?(opts, :scheduled_at) do
true -> %{scheduled_at: opts[:scheduled_at]}
false -> %{}
case Keyword.get(opts, :scheduled_at) do
%DateTime{} = datetime -> %{scheduled_at: DateTime.to_iso8601(datetime)}
nil -> %{}
value -> %{scheduled_at: value}

Copilot uses AI. Check for mistakes.
@themusicman
themusicman marked this pull request as draft July 16, 2025 11:43
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.

2 participants