Skip to content

Commit c410806

Browse files
authored
Merge pull request #6 from forwardemail/add-configurable-ctx-and-error-handling
refactor: add context support and improve error handling
2 parents 2b6b9a0 + ceace62 commit c410806

File tree

14 files changed

+436
-232
lines changed

14 files changed

+436
-232
lines changed

forwardemail/accounts.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package forwardemail
66

77
import (
8+
"context"
89
"encoding/json"
10+
"fmt"
911
"time"
1012
)
1113

@@ -26,22 +28,27 @@ type Account struct {
2628
}
2729

2830
// GetAccount retrieves the authenticated user's account information from the Forward Email API.
29-
func (c *Client) GetAccount() (*Account, error) {
30-
req, err := c.newRequest("GET", "/v1/account")
31-
if err != nil {
31+
func (c *Client) GetAccount(ctx context.Context) (*Account, error) {
32+
if ctx == nil {
33+
return nil, ErrNilContext
34+
}
35+
if err := ctx.Err(); err != nil {
3236
return nil, err
3337
}
3438

39+
req, err := c.newRequest(ctx, "GET", "/v1/account", nil)
40+
if err != nil {
41+
return nil, fmt.Errorf("failed to create request for GetAccount: %w", err)
42+
}
43+
3544
res, err := c.doRequest(req)
3645
if err != nil {
37-
return nil, err
46+
return nil, fmt.Errorf("failed to fetch account: %w", err)
3847
}
3948

4049
var item Account
41-
42-
err = json.Unmarshal(res, &item)
43-
if err != nil {
44-
return nil, err
50+
if err := json.Unmarshal(res, &item); err != nil {
51+
return nil, fmt.Errorf("failed to parse account response: %w", err)
4552
}
4653

4754
return &item, nil

forwardemail/accounts_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package forwardemail
55

66
import (
7+
"context"
78
"fmt"
89
"net/http"
910
"net/http/httptest"
@@ -61,12 +62,9 @@ func TestClient_GetAccount(t *testing.T) {
6162
}))
6263
defer svr.Close()
6364

64-
c, _ := NewClient(ClientOptions{
65-
APIKey: "test-key",
66-
APIURL: svr.URL,
67-
})
65+
c, _ := NewClient("test-key", WithAPIURL(svr.URL))
6866

69-
got, _ := c.GetAccount()
67+
got, _ := c.GetAccount(context.Background())
7068
if diff := cmp.Diff(tt.want, got); diff != "" {
7169
t.Fatalf("values are not the same %s", diff)
7270
}

forwardemail/aliases.go

Lines changed: 104 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
package forwardemail
55

66
import (
7+
"context"
78
"encoding/json"
89
"fmt"
9-
"io"
1010
"net/url"
1111
"strconv"
1212
"strings"
@@ -39,55 +39,89 @@ type AliasParameters struct {
3939
}
4040

4141
// GetAliases retrieves all email aliases for the specified domain.
42-
func (c *Client) GetAliases(domain string) ([]Alias, error) {
43-
req, err := c.newRequest("GET", fmt.Sprintf("/v1/domains/%s/aliases", domain))
44-
if err != nil {
42+
func (c *Client) GetAliases(ctx context.Context, domain string) ([]Alias, error) {
43+
if ctx == nil {
44+
return nil, ErrNilContext
45+
}
46+
if err := ctx.Err(); err != nil {
4547
return nil, err
4648
}
49+
if strings.TrimSpace(domain) == "" {
50+
return nil, ErrEmptyDomain
51+
}
52+
53+
encodedDomain := url.PathEscape(domain)
54+
55+
req, err := c.newRequest(ctx, "GET", fmt.Sprintf("/v1/domains/%s/aliases", encodedDomain), nil)
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to create request for GetAliases: %w", err)
58+
}
4759

4860
res, err := c.doRequest(req)
4961
if err != nil {
50-
return nil, err
62+
return nil, fmt.Errorf("failed to fetch aliases: %w", err)
5163
}
5264

5365
var items []Alias
54-
55-
err = json.Unmarshal(res, &items)
56-
if err != nil {
57-
return nil, err
66+
if err := json.Unmarshal(res, &items); err != nil {
67+
return nil, fmt.Errorf("failed to parse aliases response: %w", err)
5868
}
5969

6070
return items, nil
6171
}
6272

6373
// GetAlias retrieves a specific email alias by name for the specified domain.
64-
func (c *Client) GetAlias(domain, alias string) (*Alias, error) {
65-
req, err := c.newRequest("GET", fmt.Sprintf("/v1/domains/%s/aliases/%s", domain, alias))
66-
if err != nil {
74+
func (c *Client) GetAlias(ctx context.Context, domain, alias string) (*Alias, error) {
75+
if ctx == nil {
76+
return nil, ErrNilContext
77+
}
78+
if err := ctx.Err(); err != nil {
6779
return nil, err
6880
}
81+
if strings.TrimSpace(domain) == "" {
82+
return nil, ErrEmptyDomain
83+
}
84+
if strings.TrimSpace(alias) == "" {
85+
return nil, ErrEmptyAlias
86+
}
87+
88+
encodedDomain := url.PathEscape(domain)
89+
encodedAlias := url.PathEscape(alias)
90+
91+
req, err := c.newRequest(ctx, "GET", fmt.Sprintf("/v1/domains/%s/aliases/%s", encodedDomain, encodedAlias), nil)
92+
if err != nil {
93+
return nil, fmt.Errorf("failed to create request for GetAlias: %w", err)
94+
}
6995

7096
res, err := c.doRequest(req)
7197
if err != nil {
72-
return nil, err
98+
return nil, fmt.Errorf("failed to fetch alias: %w", err)
7399
}
74100

75101
var item Alias
76-
77-
err = json.Unmarshal(res, &item)
78-
if err != nil {
79-
return nil, err
102+
if err := json.Unmarshal(res, &item); err != nil {
103+
return nil, fmt.Errorf("failed to parse alias response: %w", err)
80104
}
81105

82106
return &item, nil
83107
}
84108

85109
// CreateAlias creates a new email alias for the specified domain with the given parameters.
86-
func (c *Client) CreateAlias(domain, alias string, parameters AliasParameters) (*Alias, error) {
87-
req, err := c.newRequest("POST", fmt.Sprintf("/v1/domains/%s/aliases", domain))
88-
if err != nil {
110+
func (c *Client) CreateAlias(ctx context.Context, domain, alias string, parameters AliasParameters) (*Alias, error) {
111+
if ctx == nil {
112+
return nil, ErrNilContext
113+
}
114+
if err := ctx.Err(); err != nil {
89115
return nil, err
90116
}
117+
if strings.TrimSpace(domain) == "" {
118+
return nil, ErrEmptyDomain
119+
}
120+
if strings.TrimSpace(alias) == "" {
121+
return nil, ErrEmptyAlias
122+
}
123+
124+
encodedDomain := url.PathEscape(domain)
91125

92126
params := url.Values{}
93127
params.Add("name", alias)
@@ -115,30 +149,43 @@ func (c *Client) CreateAlias(domain, alias string, parameters AliasParameters) (
115149
}
116150
}
117151

118-
req.Body = io.NopCloser(strings.NewReader(params.Encode()))
152+
req, err := c.newRequest(ctx, "POST", fmt.Sprintf("/v1/domains/%s/aliases", encodedDomain), strings.NewReader(params.Encode()))
153+
if err != nil {
154+
return nil, fmt.Errorf("failed to create request for CreateAlias: %w", err)
155+
}
156+
119157
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
120158

121159
res, err := c.doRequest(req)
122160
if err != nil {
123-
return nil, err
161+
return nil, fmt.Errorf("failed to create alias: %w", err)
124162
}
125163

126164
var item Alias
127-
128-
err = json.Unmarshal(res, &item)
129-
if err != nil {
130-
return nil, err
165+
if err := json.Unmarshal(res, &item); err != nil {
166+
return nil, fmt.Errorf("failed to parse create alias response: %w", err)
131167
}
132168

133169
return &item, nil
134170
}
135171

136172
// UpdateAlias updates an existing email alias with new parameters for the specified domain.
137-
func (c *Client) UpdateAlias(domain, alias string, parameters AliasParameters) (*Alias, error) {
138-
req, err := c.newRequest("PUT", fmt.Sprintf("/v1/domains/%s/aliases/%s", domain, alias))
139-
if err != nil {
173+
func (c *Client) UpdateAlias(ctx context.Context, domain, alias string, parameters AliasParameters) (*Alias, error) {
174+
if ctx == nil {
175+
return nil, ErrNilContext
176+
}
177+
if err := ctx.Err(); err != nil {
140178
return nil, err
141179
}
180+
if strings.TrimSpace(domain) == "" {
181+
return nil, ErrEmptyDomain
182+
}
183+
if strings.TrimSpace(alias) == "" {
184+
return nil, ErrEmptyAlias
185+
}
186+
187+
encodedDomain := url.PathEscape(domain)
188+
encodedAlias := url.PathEscape(alias)
142189

143190
params := url.Values{}
144191
params.Add("name", alias)
@@ -166,34 +213,52 @@ func (c *Client) UpdateAlias(domain, alias string, parameters AliasParameters) (
166213
}
167214
}
168215

169-
req.Body = io.NopCloser(strings.NewReader(params.Encode()))
216+
req, err := c.newRequest(ctx, "PUT", fmt.Sprintf("/v1/domains/%s/aliases/%s", encodedDomain, encodedAlias), strings.NewReader(params.Encode()))
217+
if err != nil {
218+
return nil, fmt.Errorf("failed to create request for UpdateAlias: %w", err)
219+
}
220+
170221
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
171222

172223
res, err := c.doRequest(req)
173224
if err != nil {
174-
return nil, err
225+
return nil, fmt.Errorf("failed to update alias: %w", err)
175226
}
176227

177228
var item Alias
178-
179-
err = json.Unmarshal(res, &item)
180-
if err != nil {
181-
return nil, err
229+
if err := json.Unmarshal(res, &item); err != nil {
230+
return nil, fmt.Errorf("failed to parse update alias response: %w", err)
182231
}
183232

184233
return &item, nil
185234
}
186235

187236
// DeleteAlias removes an email alias from the specified domain.
188-
func (c *Client) DeleteAlias(domain, alias string) error {
189-
req, err := c.newRequest("DELETE", fmt.Sprintf("/v1/domains/%s/aliases/%s", domain, alias))
190-
if err != nil {
237+
func (c *Client) DeleteAlias(ctx context.Context, domain, alias string) error {
238+
if ctx == nil {
239+
return ErrNilContext
240+
}
241+
if err := ctx.Err(); err != nil {
191242
return err
192243
}
244+
if strings.TrimSpace(domain) == "" {
245+
return ErrEmptyDomain
246+
}
247+
if strings.TrimSpace(alias) == "" {
248+
return ErrEmptyAlias
249+
}
250+
251+
encodedDomain := url.PathEscape(domain)
252+
encodedAlias := url.PathEscape(alias)
253+
254+
req, err := c.newRequest(ctx, "DELETE", fmt.Sprintf("/v1/domains/%s/aliases/%s", encodedDomain, encodedAlias), nil)
255+
if err != nil {
256+
return fmt.Errorf("failed to create request for DeleteAlias: %w", err)
257+
}
193258

194259
_, err = c.doRequest(req)
195260
if err != nil {
196-
return err
261+
return fmt.Errorf("failed to delete alias: %w", err)
197262
}
198263

199264
return nil

0 commit comments

Comments
 (0)