Skip to content

Commit a6faf98

Browse files
authored
Merge pull request #140 from ajinkyasurya/ajinkyasurya/pollErrorHandler
feat: Add a configurable error handler for environment updates. Return response codes on HTTP errors
2 parents c3512cf + 880cf36 commit a6faf98

File tree

4 files changed

+72
-12
lines changed

4 files changed

+72
-12
lines changed

client.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Client struct {
3131
ctxAnalytics context.Context
3232
log Logger
3333
offlineHandler OfflineHandler
34+
errorHandler func(handler *FlagsmithAPIError)
3435
}
3536

3637
// NewClient creates instance of Client with given configuration.
@@ -147,7 +148,8 @@ func (c *Client) GetIdentitySegments(identifier string, traits []*Trait) ([]*seg
147148
// NOTE: This method only works with Edge API endpoint.
148149
func (c *Client) BulkIdentify(ctx context.Context, batch []*IdentityTraits) error {
149150
if len(batch) > bulkIdentifyMaxCount {
150-
return &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: batch size must be less than %d", bulkIdentifyMaxCount)}
151+
msg := fmt.Sprintf("flagsmith: batch size must be less than %d", bulkIdentifyMaxCount)
152+
return &FlagsmithAPIError{Msg: msg}
151153
}
152154

153155
body := struct {
@@ -160,13 +162,16 @@ func (c *Client) BulkIdentify(ctx context.Context, batch []*IdentityTraits) erro
160162
ForceContentType("application/json").
161163
Post(c.config.baseURL + "bulk-identities/")
162164
if resp.StatusCode() == 404 {
163-
return &FlagsmithAPIError{msg: "flagsmith: Bulk identify endpoint not found; Please make sure you are using Edge API endpoint"}
165+
msg := "flagsmith: Bulk identify endpoint not found; Please make sure you are using Edge API endpoint"
166+
return &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
164167
}
165168
if err != nil {
166-
return &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)}
169+
msg := fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)
170+
return &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
167171
}
168172
if !resp.IsSuccess() {
169-
return &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())}
173+
msg := fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())
174+
return &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
170175
}
171176
return nil
172177
}
@@ -179,10 +184,12 @@ func (c *Client) GetEnvironmentFlagsFromAPI(ctx context.Context) (Flags, error)
179184
ForceContentType("application/json").
180185
Get(c.config.baseURL + "flags/")
181186
if err != nil {
182-
return Flags{}, &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)}
187+
msg := fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)
188+
return Flags{}, &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
183189
}
184190
if !resp.IsSuccess() {
185-
return Flags{}, &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())}
191+
msg := fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())
192+
return Flags{}, &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
186193
}
187194
return makeFlagsFromAPIFlags(resp.Body(), c.analyticsProcessor, c.defaultFlagHandler)
188195
}
@@ -200,10 +207,12 @@ func (c *Client) GetIdentityFlagsFromAPI(ctx context.Context, identifier string,
200207
ForceContentType("application/json").
201208
Post(c.config.baseURL + "identities/")
202209
if err != nil {
203-
return Flags{}, &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)}
210+
msg := fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)
211+
return Flags{}, &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
204212
}
205213
if !resp.IsSuccess() {
206-
return Flags{}, &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())}
214+
msg := fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())
215+
return Flags{}, &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
207216
}
208217
return makeFlagsfromIdentityAPIJson(resp.Body(), c.analyticsProcessor, c.defaultFlagHandler)
209218
}
@@ -267,10 +276,20 @@ func (c *Client) UpdateEnvironment(ctx context.Context) error {
267276
Get(c.config.baseURL + "environment-document/")
268277

269278
if err != nil {
270-
return &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)}
279+
msg := fmt.Sprintf("flagsmith: error performing request to Flagsmith API: %s", err)
280+
f := &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
281+
if c.errorHandler != nil {
282+
c.errorHandler(f)
283+
}
284+
return f
271285
}
272286
if resp.StatusCode() != 200 {
273-
return &FlagsmithAPIError{msg: fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())}
287+
msg := fmt.Sprintf("flagsmith: unexpected response from Flagsmith API: %s", resp.Status())
288+
f := &FlagsmithAPIError{Msg: msg, Err: err, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
289+
if c.errorHandler != nil {
290+
c.errorHandler(f)
291+
}
292+
return f
274293
}
275294
c.environment.Store(&env)
276295
identitiesWithOverrides := make(map[string]identities.IdentityModel)

client_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,3 +675,34 @@ func TestOfflineHandlerIsUsedWhenRequestFails(t *testing.T) {
675675
assert.Equal(t, fixtures.Feature1ID, allFlags[0].FeatureID)
676676
assert.Equal(t, fixtures.Feature1Value, allFlags[0].Value)
677677
}
678+
679+
func TestPollErrorHandlerIsUsedWhenPollFails(t *testing.T) {
680+
// Given
681+
ctx := context.Background()
682+
var capturedError error
683+
var statusCode int
684+
var status string
685+
686+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
687+
rw.WriteHeader(http.StatusInternalServerError)
688+
}))
689+
defer server.Close()
690+
691+
// When
692+
client := flagsmith.NewClient(fixtures.EnvironmentAPIKey,
693+
flagsmith.WithBaseURL(server.URL+"/api/v1/"),
694+
flagsmith.WithErrorHandler(func(handler *flagsmith.FlagsmithAPIError) {
695+
capturedError = handler.Err
696+
statusCode = handler.ResponseStatusCode
697+
status = handler.ResponseStatus
698+
}),
699+
)
700+
701+
// when
702+
_ = client.UpdateEnvironment(ctx)
703+
704+
// Then
705+
assert.Equal(t, capturedError, nil)
706+
assert.Equal(t, statusCode, 500)
707+
assert.Equal(t, status, "500 Internal Server Error")
708+
}

errors.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ type FlagsmithClientError struct {
55
}
66

77
type FlagsmithAPIError struct {
8-
msg string
8+
Msg string
9+
Err error
10+
ResponseStatusCode int
11+
ResponseStatus string
912
}
1013

1114
func (e FlagsmithClientError) Error() string {
1215
return e.msg
1316
}
1417

1518
func (e FlagsmithAPIError) Error() string {
16-
return e.msg
19+
return e.Msg
1720
}

options.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,10 @@ func WithOfflineMode() Option {
117117
c.config.offlineMode = true
118118
}
119119
}
120+
121+
// WithErrorHandler provides a way to handle errors that occur during update of an environment.
122+
func WithErrorHandler(handler func(handler *FlagsmithAPIError)) Option {
123+
return func(c *Client) {
124+
c.errorHandler = handler
125+
}
126+
}

0 commit comments

Comments
 (0)