Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Schedule, cancel and update email #38

Merged
merged 4 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 86 additions & 3 deletions emails.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"net/http"
)

// SendEmailRequest is the request object for the SendEmail call.
// SendEmailRequest is the request object for the Send call.
//
// See also https://resend.com/docs/api-reference/emails/send-email
type SendEmailRequest struct {
Expand All @@ -21,14 +21,33 @@ type SendEmailRequest struct {
Tags []Tag `json:"tags,omitempty"`
Attachments []*Attachment `json:"attachments,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
ScheduledAt string `json:"scheduled_at,omitempty"`
}

// SendEmailResponse is the response from the SendEmail call.
// CancelScheduledEmailResponse is the response from the Cancel call.
type CancelScheduledEmailResponse struct {
Id string `json:"id"`
Object string `json:"object"`
}

// SendEmailResponse is the response from the Send call.
type SendEmailResponse struct {
Id string `json:"id"`
}

// Email provides the structure for the response from the GetEmail call.
// UpdateEmailRequest is the request object for the Update call.
type UpdateEmailRequest struct {
Id string `json:"id"`
ScheduledAt string `json:"scheduled_at"`
}

// UpdateEmailResponse is the type that represents the response from the Update call.
type UpdateEmailResponse struct {
Id string `json:"id"`
Object string `json:"object"`
}

// Email provides the structure for the response from the Get call.
type Email struct {
Id string `json:"id"`
Object string `json:"object"`
Expand Down Expand Up @@ -88,6 +107,10 @@ func (a *Attachment) MarshalJSON() ([]byte, error) {
}

type EmailsSvc interface {
CancelWithContext(ctx context.Context, emailID string) (*CancelScheduledEmailResponse, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

it's nice that we are providing the methods with and without context 👍

Cancel(emailID string) (*CancelScheduledEmailResponse, error)
UpdateWithContext(ctx context.Context, params *UpdateEmailRequest) (*UpdateEmailResponse, error)
Update(params *UpdateEmailRequest) (*UpdateEmailResponse, error)
SendWithContext(ctx context.Context, params *SendEmailRequest) (*SendEmailResponse, error)
Send(params *SendEmailRequest) (*SendEmailResponse, error)
GetWithContext(ctx context.Context, emailID string) (*Email, error)
Expand All @@ -98,6 +121,66 @@ type EmailsSvcImpl struct {
client *Client
}

// Cancel cancels an email by ID
// https://resend.com/docs/api-reference/emails/cancel-email
func (s *EmailsSvcImpl) Cancel(emailID string) (*CancelScheduledEmailResponse, error) {
return s.CancelWithContext(context.Background(), emailID)
}

// CancelWithContext cancels an email by ID
// https://resend.com/docs/api-reference/emails/cancel-email
func (s *EmailsSvcImpl) CancelWithContext(ctx context.Context, emailID string) (*CancelScheduledEmailResponse, error) {
path := "emails/" + emailID + "/cancel"

// Prepare request
req, err := s.client.NewRequest(ctx, http.MethodPost, path, nil)
if err != nil {
return nil, ErrFailedToCreateEmailsSendRequest
}

// Build response recipient obj
resp := new(CancelScheduledEmailResponse)

// Send Request
_, err = s.client.Perform(req, resp)

if err != nil {
return nil, err
}

return resp, nil
}

// Update updates an email with the given params
// https://resend.com/docs/api-reference/emails/update-email
func (s *EmailsSvcImpl) Update(params *UpdateEmailRequest) (*UpdateEmailResponse, error) {
return s.UpdateWithContext(context.Background(), params)
}

// UpdateWithContext sends an email with the given params
// https://resend.com/docs/api-reference/emails/update-email
func (s *EmailsSvcImpl) UpdateWithContext(ctx context.Context, params *UpdateEmailRequest) (*UpdateEmailResponse, error) {
path := "emails/" + params.Id

// Prepare request
req, err := s.client.NewRequest(ctx, http.MethodPatch, path, params)
if err != nil {
return nil, ErrFailedToCreateUpdateEmailRequest
}

// Build response recipient obj
updateEmailResponse := new(UpdateEmailResponse)

// Send Request
_, err = s.client.Perform(req, updateEmailResponse)

if err != nil {
return nil, err
}

return updateEmailResponse, nil
}

// SendWithContext sends an email with the given params
// https://resend.com/docs/api-reference/emails/send-email
func (s *EmailsSvcImpl) SendWithContext(ctx context.Context, params *SendEmailRequest) (*SendEmailResponse, error) {
Expand Down
54 changes: 54 additions & 0 deletions emails_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,35 @@ func teardown() {
server.Close()
}

func TestScheduleEmail(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/emails", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)

ret := &SendEmailResponse{
Id: "1923781293",
}
err := json.NewEncoder(w).Encode(&ret)
if err != nil {
panic(err)
}
})

req := &SendEmailRequest{
To: []string{"[email protected]"},
ScheduledAt: "2024-09-05T11:52:01.858Z",
}
resp, err := client.Emails.Send(req)
if err != nil {
t.Errorf("Emails.Send returned error: %v", err)
}
assert.Equal(t, resp.Id, "1923781293")
}

func TestSendEmail(t *testing.T) {
setup()
defer teardown()
Expand Down Expand Up @@ -132,6 +161,31 @@ func TestGetEmail(t *testing.T) {
assert.Equal(t, resp.Subject, "Hello World")
}

func TestCancelScheduledEmail(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/emails/dacf4072-4119-4d88-932f-6202748ac7c8/cancel", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)

ret := `
{
"id": "dacf4072-4119-4d88-932f-6202748ac7c8",
"object": "email"
}`
fmt.Fprintf(w, ret)
})

resp, err := client.Emails.Cancel("dacf4072-4119-4d88-932f-6202748ac7c8")
if err != nil {
t.Errorf("Emails.Cancel returned error: %v", err)
}
assert.Equal(t, resp.Id, "dacf4072-4119-4d88-932f-6202748ac7c8")
assert.Equal(t, resp.Object, "email")
}

func testMethod(t *testing.T, r *http.Request, expected string) {
if expected != r.Method {
t.Errorf("Request method = %v, expected %v", r.Method, expected)
Expand Down
5 changes: 3 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var (

// EmailsSvc errors
var (
ErrFailedToCreateEmailsSendRequest = errors.New("[ERROR]: Failed to create SendEmail request")
ErrFailedToCreateEmailsGetRequest = errors.New("[ERROR]: Failed to create GetEmail request")
ErrFailedToCreateUpdateEmailRequest = errors.New("[ERROR]: Failed to create UpdateEmail request")
ErrFailedToCreateEmailsSendRequest = errors.New("[ERROR]: Failed to create SendEmail request")
ErrFailedToCreateEmailsGetRequest = errors.New("[ERROR]: Failed to create GetEmail request")
)
49 changes: 49 additions & 0 deletions examples/schedule_email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package examples

import (
"context"
"fmt"
"os"

"github.com/resend/resend-go/v2"
)

func scheduleEmail() {
ctx := context.TODO()
apiKey := os.Getenv("RESEND_API_KEY")

client := resend.NewClient(apiKey)

// Schedule the email
params := &resend.SendEmailRequest{
To: []string{"[email protected]"},
From: "[email protected]",
Text: "hello world",
Subject: "Hello from Golang",
ScheduledAt: "2024-09-05T11:52:01.858Z",
}

sent, err := client.Emails.SendWithContext(ctx, params)
if err != nil {
panic(err)
}
fmt.Println(sent.Id)

updateParams := &resend.UpdateEmailRequest{
Id: sent.Id,
ScheduledAt: "2024-11-05T11:52:01.858Z",
}

// Update the scheduled email
updatedEmail, err := client.Emails.UpdateWithContext(ctx, updateParams)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", updatedEmail)

canceled, err := client.Emails.CancelWithContext(ctx, "32723fee-8502-4b58-8b5e-bfd98f453ced")
if err != nil {
panic(err)
}
fmt.Printf("%v\n", canceled)
}
2 changes: 1 addition & 1 deletion resend.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

const (
version = "2.10.0"
version = "2.11.0"
userAgent = "resend-go/" + version
contentType = "application/json"
)
Expand Down
Loading