Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Contact management:

- Contacts — create, get, update (upsert) & delete by ID or email — [`examples/contacts`](examples/contacts)
- Contact events — record custom events for a contact — [`examples/contact-events`](examples/contact-events)
- Contact lists — create, list, get, rename & delete — [`examples/contact-lists`](examples/contact-lists)
- Contact fields — manage custom fields (text, number, boolean, date) — [`examples/contact-fields`](examples/contact-fields)

## Errors

Expand Down
6 changes: 6 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ type Client struct {
Contacts *ContactsService
// ContactEvents records custom events for contacts.
ContactEvents *ContactEventsService
// ContactLists manages contact lists.
ContactLists *ContactListsService
// ContactFields manages custom contact fields.
ContactFields *ContactFieldsService
}

// Ptr returns a pointer to v, for setting optional pointer request fields such
Expand Down Expand Up @@ -149,6 +153,8 @@ func NewClient(token string, opts ...Option) (*Client, error) {
c.Permissions = &PermissionsService{client: c}
c.Contacts = &ContactsService{client: c}
c.ContactEvents = &ContactEventsService{client: c}
c.ContactLists = &ContactListsService{client: c}
c.ContactFields = &ContactFieldsService{client: c}

return c, nil
}
Expand Down
82 changes: 82 additions & 0 deletions contact_fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package mailtrap

import (
"context"
"fmt"
"net/http"
)

// ContactFieldsService manages custom contact fields.
type ContactFieldsService struct {
client *Client
}

// Contact field data types for ContactField.DataType.
const (
ContactFieldTypeText = "text"
ContactFieldTypeInteger = "integer"
ContactFieldTypeFloat = "float"
ContactFieldTypeBoolean = "boolean"
ContactFieldTypeDate = "date"
)

// ContactField is a custom field that can be set on contacts.
type ContactField struct {
ID int64 `json:"id"`
Name string `json:"name"`
// DataType is the field's value type.
DataType string `json:"data_type"`
// MergeTag personalizes campaigns with the field's per-contact value.
MergeTag string `json:"merge_tag"`
}

// CreateContactFieldRequest is the payload for creating a contact field. All
// fields are required.
type CreateContactFieldRequest struct {
Name string `json:"name"`
DataType string `json:"data_type"`
MergeTag string `json:"merge_tag"`
}

// UpdateContactFieldRequest changes a contact field's name and merge tag; the
// data type is immutable.
type UpdateContactFieldRequest struct {
Name string `json:"name"`
MergeTag string `json:"merge_tag"`
}

// List returns all contact fields.
func (s *ContactFieldsService) List(ctx context.Context) ([]*ContactField, *Response, error) {
var fields []*ContactField
resp, err := s.client.do(ctx, HostGeneral, http.MethodGet, "/api/contacts/fields", nil, nil, &fields)
return fields, resp, err
}

// Get returns a contact field by ID.
func (s *ContactFieldsService) Get(ctx context.Context, fieldID int64) (*ContactField, *Response, error) {
path := fmt.Sprintf("/api/contacts/fields/%d", fieldID)
field := new(ContactField)
resp, err := s.client.do(ctx, HostGeneral, http.MethodGet, path, nil, nil, field)
return field, resp, err
}

// Create adds a contact field.
func (s *ContactFieldsService) Create(ctx context.Context, req *CreateContactFieldRequest) (*ContactField, *Response, error) {
field := new(ContactField)
resp, err := s.client.do(ctx, HostGeneral, http.MethodPost, "/api/contacts/fields", nil, req, field)
return field, resp, err
}

// Update changes a contact field's name and merge tag.
func (s *ContactFieldsService) Update(ctx context.Context, fieldID int64, req *UpdateContactFieldRequest) (*ContactField, *Response, error) {
path := fmt.Sprintf("/api/contacts/fields/%d", fieldID)
field := new(ContactField)
resp, err := s.client.do(ctx, HostGeneral, http.MethodPatch, path, nil, req, field)
return field, resp, err
}

// Delete removes a contact field by ID.
func (s *ContactFieldsService) Delete(ctx context.Context, fieldID int64) (*Response, error) {
path := fmt.Sprintf("/api/contacts/fields/%d", fieldID)
return s.client.do(ctx, HostGeneral, http.MethodDelete, path, nil, nil, nil)
}
94 changes: 94 additions & 0 deletions contact_fields_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package mailtrap_test

import (
"context"
"net/http"
"testing"

"github.com/mailtrap/mailtrap-go"
)

func TestContactFields_List(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("GET /api/contacts/fields", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`[{"id":6730,"name":"First name","data_type":"text","merge_tag":"first_name"}]`))
})

fields, _, err := client.ContactFields.List(context.Background())
if err != nil {
t.Fatalf("List: %v", err)
}
if len(fields) != 1 || fields[0].DataType != mailtrap.ContactFieldTypeText {
t.Fatalf("fields = %+v", fields)
}
}

func TestContactFields_Get(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("GET /api/contacts/fields/6730", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"id":6730,"name":"First name","data_type":"text","merge_tag":"first_name"}`))
})

field, _, err := client.ContactFields.Get(context.Background(), 6730)
if err != nil {
t.Fatalf("Get: %v", err)
}
if field.MergeTag != "first_name" {
t.Errorf("field = %+v", field)
}
}

func TestContactFields_Create(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("POST /api/contacts/fields", func(w http.ResponseWriter, r *http.Request) {
wantJSONBody(t, r, `{"name":"Age","data_type":"integer","merge_tag":"age"}`)
w.WriteHeader(http.StatusCreated)
_, _ = w.Write([]byte(`{"id":6731,"name":"Age","data_type":"integer","merge_tag":"age"}`))
})

field, _, err := client.ContactFields.Create(context.Background(), &mailtrap.CreateContactFieldRequest{
Name: "Age",
DataType: mailtrap.ContactFieldTypeInteger,
MergeTag: "age",
})
if err != nil {
t.Fatalf("Create: %v", err)
}
if field.ID != 6731 || field.DataType != mailtrap.ContactFieldTypeInteger {
t.Errorf("field = %+v", field)
}
}

func TestContactFields_Update(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("PATCH /api/contacts/fields/6731", func(w http.ResponseWriter, r *http.Request) {
wantJSONBody(t, r, `{"name":"Years","merge_tag":"years"}`)
_, _ = w.Write([]byte(`{"id":6731,"name":"Years","data_type":"integer","merge_tag":"years"}`))
})

field, _, err := client.ContactFields.Update(context.Background(), 6731, &mailtrap.UpdateContactFieldRequest{
Name: "Years",
MergeTag: "years",
})
if err != nil {
t.Fatalf("Update: %v", err)
}
if field.Name != "Years" || field.MergeTag != "years" {
t.Errorf("field = %+v", field)
}
}

func TestContactFields_Delete(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("DELETE /api/contacts/fields/6731", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNoContent)
})

resp, err := client.ContactFields.Delete(context.Background(), 6731)
if err != nil {
t.Fatalf("Delete: %v", err)
}
if resp.StatusCode != http.StatusNoContent {
t.Errorf("status = %d", resp.StatusCode)
}
}
56 changes: 56 additions & 0 deletions contact_lists.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package mailtrap

import (
"context"
"fmt"
"net/http"
)

// ContactListsService manages contact lists.
type ContactListsService struct {
client *Client
}

// ContactList is a named list that groups contacts.
type ContactList struct {
ID int64 `json:"id"`
Name string `json:"name"`
}

// List returns all contact lists.
func (s *ContactListsService) List(ctx context.Context) ([]*ContactList, *Response, error) {
var lists []*ContactList
resp, err := s.client.do(ctx, HostGeneral, http.MethodGet, "/api/contacts/lists", nil, nil, &lists)
return lists, resp, err
}

// Get returns a contact list by ID.
func (s *ContactListsService) Get(ctx context.Context, listID int64) (*ContactList, *Response, error) {
path := fmt.Sprintf("/api/contacts/lists/%d", listID)
list := new(ContactList)
resp, err := s.client.do(ctx, HostGeneral, http.MethodGet, path, nil, nil, list)
return list, resp, err
}

// Create adds a contact list with the given name.
func (s *ContactListsService) Create(ctx context.Context, name string) (*ContactList, *Response, error) {
body := map[string]string{"name": name}
list := new(ContactList)
resp, err := s.client.do(ctx, HostGeneral, http.MethodPost, "/api/contacts/lists", nil, body, list)
return list, resp, err
}

// Update renames a contact list.
func (s *ContactListsService) Update(ctx context.Context, listID int64, name string) (*ContactList, *Response, error) {
path := fmt.Sprintf("/api/contacts/lists/%d", listID)
body := map[string]string{"name": name}
list := new(ContactList)
resp, err := s.client.do(ctx, HostGeneral, http.MethodPatch, path, nil, body, list)
return list, resp, err
}

// Delete removes a contact list by ID.
func (s *ContactListsService) Delete(ctx context.Context, listID int64) (*Response, error) {
path := fmt.Sprintf("/api/contacts/lists/%d", listID)
return s.client.do(ctx, HostGeneral, http.MethodDelete, path, nil, nil, nil)
}
84 changes: 84 additions & 0 deletions contact_lists_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package mailtrap_test

import (
"context"
"net/http"
"testing"
)

func TestContactLists_List(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("GET /api/contacts/lists", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`[{"id":26730,"name":"Customers"},{"id":26731,"name":"Old Contacts"}]`))
})

lists, _, err := client.ContactLists.List(context.Background())
if err != nil {
t.Fatalf("List: %v", err)
}
if len(lists) != 2 || lists[0].ID != 26730 || lists[0].Name != "Customers" {
t.Fatalf("lists = %+v", lists)
}
}

func TestContactLists_Get(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("GET /api/contacts/lists/26730", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"id":26730,"name":"Customers"}`))
})

list, _, err := client.ContactLists.Get(context.Background(), 26730)
if err != nil {
t.Fatalf("Get: %v", err)
}
if list.Name != "Customers" {
t.Errorf("list = %+v", list)
}
}

func TestContactLists_Create(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("POST /api/contacts/lists", func(w http.ResponseWriter, r *http.Request) {
wantJSONBody(t, r, `{"name":"Customers"}`)
_, _ = w.Write([]byte(`{"id":26730,"name":"Customers"}`))
})

list, _, err := client.ContactLists.Create(context.Background(), "Customers")
if err != nil {
t.Fatalf("Create: %v", err)
}
if list.ID != 26730 {
t.Errorf("list = %+v", list)
}
}

func TestContactLists_Update(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("PATCH /api/contacts/lists/26730", func(w http.ResponseWriter, r *http.Request) {
wantJSONBody(t, r, `{"name":"Former Customers"}`)
_, _ = w.Write([]byte(`{"id":26730,"name":"Former Customers"}`))
})

list, _, err := client.ContactLists.Update(context.Background(), 26730, "Former Customers")
if err != nil {
t.Fatalf("Update: %v", err)
}
if list.Name != "Former Customers" {
t.Errorf("list = %+v", list)
}
}

func TestContactLists_Delete(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("DELETE /api/contacts/lists/26730", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNoContent)
})

resp, err := client.ContactLists.Delete(context.Background(), 26730)
if err != nil {
t.Fatalf("Delete: %v", err)
}
if resp.StatusCode != http.StatusNoContent {
t.Errorf("status = %d", resp.StatusCode)
}
}
48 changes: 48 additions & 0 deletions examples/contact-fields/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"context"
"fmt"
"log"
"os"

"github.com/mailtrap/mailtrap-go"
)

func main() {
client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"))
if err != nil {
log.Fatal(err)
}

ctx := context.Background()

field, _, err := client.ContactFields.Create(ctx, &mailtrap.CreateContactFieldRequest{
Name: "Age",
DataType: mailtrap.ContactFieldTypeInteger,
MergeTag: "age",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("created field %d (%s, %s)\n", field.ID, field.Name, field.DataType)

fields, _, err := client.ContactFields.List(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("account has %d field(s)\n", len(fields))

// The data type is immutable; only name and merge tag can change.
if _, _, err = client.ContactFields.Update(ctx, field.ID, &mailtrap.UpdateContactFieldRequest{
Name: "Years",
MergeTag: "years",
}); err != nil {
log.Fatal(err)
}

if _, err = client.ContactFields.Delete(ctx, field.ID); err != nil {
log.Fatal(err)
}
fmt.Println("deleted field")
}
Loading