Skip to content

Commit 1a3dc5c

Browse files
authored
Merge pull request #46 from messagebird/add-groups
Add groups
2 parents 1e9cd61 + f00162c commit 1a3dc5c

15 files changed

+628
-9
lines changed

contact/testdata/contactListObject.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@
5757
"updatedDatetime": null
5858
}
5959
]
60-
}
60+
}

contact/testdata/contactObject.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@
2020
},
2121
"createdDatetime": "2018-07-13T10:34:08+00:00",
2222
"updatedDatetime": "2018-07-13T10:44:08+00:00"
23-
}
23+
}

contact/testdata/contactObjectWithCustomDetails.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@
2020
},
2121
"createdDatetime": "2018-07-13T10:34:08+00:00",
2222
"updatedDatetime": "2018-07-13T10:44:08+00:00"
23-
}
23+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"msisdn":"31612345678","firstName":"Foo","lastName":"Bar","custom1":"First","custom2":"Second"}
1+
{"msisdn":"31612345678","firstName":"Foo","lastName":"Bar","custom1":"First","custom2":"Second"}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"custom1":"Foo","custom4":"Bar"}
1+
{"custom1":"Foo","custom4":"Bar"}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"msisdn":"31687654321"}
1+
{"msisdn":"31687654321"}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"firstName":"Message","lastName":"Bird"}
1+
{"firstName":"Message","lastName":"Bird"}

group/group.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package group
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
"net/url"
8+
"strconv"
9+
"strings"
10+
"time"
11+
12+
messagebird "github.com/messagebird/go-rest-api"
13+
"github.com/messagebird/go-rest-api/contact"
14+
)
15+
16+
// Group gets returned by the API.
17+
type Group struct {
18+
ID string
19+
HRef string
20+
Name string
21+
Contacts struct {
22+
TotalCount int
23+
HRef string
24+
}
25+
CreatedDatetime time.Time
26+
UpdatedDatetime time.Time
27+
}
28+
29+
type GroupList struct {
30+
Offset int
31+
Limit int
32+
Count int
33+
TotalCount int
34+
Links struct {
35+
First string
36+
Previous string
37+
Next string
38+
Last string
39+
}
40+
Items []Group
41+
}
42+
43+
// ListOptions can be used to set pagination options in List() and ListContacts().
44+
type ListOptions struct {
45+
Limit, Offset int
46+
}
47+
48+
// Request represents a contact for write operations, e.g. for creating a new
49+
// group or updating an existing one.
50+
type Request struct {
51+
Name string `json:"name"`
52+
}
53+
54+
const (
55+
// path represents the path to the Groups resource.
56+
path = "groups"
57+
58+
// contactPath represents the path to the Contacts resource within Groups.
59+
contactPath = "contacts"
60+
)
61+
62+
// maximumContactsPerRequest is the maxium number of contacts that can be
63+
// created in a single request.
64+
const maximumContactsPerRequest = 50
65+
66+
// DefaultListOptions provides reasonable values for List().
67+
var DefaultListOptions = &ListOptions{
68+
Limit: 10,
69+
Offset: 0,
70+
}
71+
72+
func Create(c *messagebird.Client, request *Request) (*Group, error) {
73+
if err := validateCreate(request); err != nil {
74+
return nil, err
75+
}
76+
77+
group := &Group{}
78+
if err := c.Request(group, http.MethodPost, path, request); err != nil {
79+
return nil, err
80+
}
81+
82+
return group, nil
83+
}
84+
85+
func validateCreate(request *Request) error {
86+
if request.Name == "" {
87+
return errors.New("name is required")
88+
}
89+
90+
return nil
91+
}
92+
93+
// Delete attempts deleting the group with the provided ID. If nil is returned,
94+
// the resource was deleted successfully.
95+
func Delete(c *messagebird.Client, id string) error {
96+
return c.Request(nil, http.MethodDelete, path+"/"+id, nil)
97+
}
98+
99+
// List retrieves a paginated list of groups, based on the options provided.
100+
// It's worth noting DefaultListOptions.
101+
func List(c *messagebird.Client, options *ListOptions) (*GroupList, error) {
102+
query, err := listQuery(options)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
groupList := &GroupList{}
108+
if err := c.Request(groupList, http.MethodGet, path+"?"+query, nil); err != nil {
109+
return nil, err
110+
}
111+
112+
return groupList, nil
113+
}
114+
115+
func listQuery(options *ListOptions) (string, error) {
116+
if options.Limit < 10 {
117+
return "", fmt.Errorf("minimum limit is 10, got %d", options.Limit)
118+
}
119+
120+
if options.Offset < 0 {
121+
return "", fmt.Errorf("offset can not be negative")
122+
}
123+
124+
values := &url.Values{}
125+
values.Set("limit", strconv.Itoa(options.Limit))
126+
values.Set("offset", strconv.Itoa(options.Offset))
127+
128+
return values.Encode(), nil
129+
}
130+
131+
// Read retrieves the information of an existing group.
132+
func Read(c *messagebird.Client, id string) (*Group, error) {
133+
group := &Group{}
134+
if err := c.Request(group, http.MethodGet, path+"/"+id, nil); err != nil {
135+
return nil, err
136+
}
137+
138+
return group, nil
139+
}
140+
141+
// Update overrides the group with any values provided in request.
142+
func Update(c *messagebird.Client, id string, request *Request) error {
143+
if err := validateUpdate(request); err != nil {
144+
return err
145+
}
146+
147+
return c.Request(nil, http.MethodPatch, path+"/"+id, request)
148+
}
149+
150+
func validateUpdate(request *Request) error {
151+
if request.Name == "" {
152+
return errors.New("name is required")
153+
}
154+
155+
return nil
156+
}
157+
158+
// AddContacts adds a maximum of 50 contacts to the group.
159+
func AddContacts(c *messagebird.Client, groupID string, contactIDs []string) error {
160+
if err := validateAddContacts(contactIDs); err != nil {
161+
return err
162+
}
163+
164+
query := addContactsQuery(contactIDs)
165+
formattedPath := fmt.Sprintf("%s/%s/%s?%s", path, groupID, contactPath, query)
166+
167+
return c.Request(nil, http.MethodGet, formattedPath, nil)
168+
}
169+
170+
func validateAddContacts(contactIDs []string) error {
171+
count := len(contactIDs)
172+
173+
// len(nil) == 0: https://golang.org/ref/spec#Length_and_capacity
174+
if count == 0 {
175+
return fmt.Errorf("at least one contactID is required")
176+
}
177+
178+
if count > maximumContactsPerRequest {
179+
return fmt.Errorf("can not add more than %d contacts per request, got %d", maximumContactsPerRequest, count)
180+
}
181+
182+
return nil
183+
}
184+
185+
// addContactsQuery gets a query string to add contacts to a group. We're using
186+
// the alternative "/foo?_method=PUT&key=value" format to send the contact IDs
187+
// as GET params. Sending these in the request body would require a painful
188+
// workaround, as client.Request sends request bodies as JSON by default. See
189+
// also: https://developers.messagebird.com/docs/alternatives.
190+
//
191+
// It should also be noted that we're intentionally not using url.Values for
192+
// building the query string: the API expects `ids[]=foo&ids[]=bar` format,
193+
// while url.Values encodes to `ids=foo&ids=bar`.
194+
func addContactsQuery(contactIDs []string) string {
195+
// Slice's length is one bigger than len(IDs) for the _method param.
196+
params := make([]string, 0, len(contactIDs)+1)
197+
params = append(params, "_method="+http.MethodPut)
198+
199+
for _, contactID := range contactIDs {
200+
params = append(params, "ids[]="+contactID)
201+
}
202+
203+
return strings.Join(params, "&")
204+
}
205+
206+
// ListContacts lists the contacts that are a member of a group.
207+
func ListContacts(c *messagebird.Client, groupID string, options *ListOptions) (*contact.ContactList, error) {
208+
query, err := listQuery(options)
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
formattedPath := fmt.Sprintf("%s/%s/%s?%s", path, groupID, contactPath, query)
214+
215+
contacts := &contact.ContactList{}
216+
if err = c.Request(contacts, http.MethodGet, formattedPath, nil); err != nil {
217+
return nil, err
218+
}
219+
220+
return contacts, nil
221+
}
222+
223+
// RemoveContact removes the contact from a group. If nil is returned, the
224+
// operation was successful.
225+
func RemoveContact(c *messagebird.Client, groupID, contactID string) error {
226+
formattedPath := fmt.Sprintf("%s/%s/contacts/%s", path, groupID, contactID)
227+
228+
return c.Request(nil, http.MethodDelete, formattedPath, nil)
229+
}

0 commit comments

Comments
 (0)