Skip to content

Commit

Permalink
feat: Schedule, cancel and update email (#38)
Browse files Browse the repository at this point in the history
Co-authored-by: Felipe Volpone <[email protected]>
  • Loading branch information
drish and felipevolpone authored Aug 14, 2024
1 parent b9dac9d commit 37ced1d
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 6 deletions.
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)
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 updates 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

0 comments on commit 37ced1d

Please sign in to comment.