Skip to content

Commit

Permalink
feat!: add overwrite ability for retry conditions and hooks #868
Browse files Browse the repository at this point in the history
- refactor retry conditions and hooks into accepting multiple values
  • Loading branch information
jeevatkm committed Jan 5, 2025
1 parent 29797dd commit 4311699
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 49 deletions.
30 changes: 19 additions & 11 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,8 @@ func (c *Client) R() *Request {
debugLogCurlCmd: c.debugLogCurlCmd,
unescapeQueryParams: c.unescapeQueryParams,
credentials: c.credentials,
retryConditions: slices.Clone(c.retryConditions),
retryHooks: slices.Clone(c.retryHooks),
}

if c.ctx != nil {
Expand Down Expand Up @@ -1362,16 +1364,18 @@ func (c *Client) RetryConditions() []RetryConditionFunc {
return c.retryConditions
}

// AddRetryCondition method adds a retry condition function to an array of functions
// that are checked to determine if the request is retried. The request will
// retry if any functions return true and the error is nil.
// AddRetryConditions method adds one or more retry condition functions into the request.
// These retry conditions are executed to determine if the request can be retried.
// The request will retry if any functions return `true`, otherwise return `false`.
//
// NOTE: These retry conditions are applied on all requests made using this Client.
// For [Request] specific retry conditions, check [Request.AddRetryCondition]
func (c *Client) AddRetryCondition(condition RetryConditionFunc) *Client {
// NOTE:
// - The client-level retry conditions are applied to all requests.
// - The request-level retry conditions are executed first before client-level
// retry conditions. See [Request.AddRetryConditions], [Request.SetRetryConditions]
func (c *Client) AddRetryConditions(conditions ...RetryConditionFunc) *Client {
c.lock.Lock()
defer c.lock.Unlock()
c.retryConditions = append(c.retryConditions, condition)
c.retryConditions = append(c.retryConditions, conditions...)
return c
}

Expand All @@ -1382,12 +1386,16 @@ func (c *Client) RetryHooks() []RetryHookFunc {
return c.retryHooks
}

// AddRetryHook adds a side-effecting retry hook to an array of hooks
// that will be executed on each retry.
func (c *Client) AddRetryHook(hook RetryHookFunc) *Client {
// AddRetryHooks method adds one or more side-effecting retry hooks to an array
// of hooks that will be executed on each retry.
//
// NOTE:
// - All the retry hooks are executed on request retry.
// - The request-level retry hooks are executed first before client-level hooks.
func (c *Client) AddRetryHooks(hooks ...RetryHookFunc) *Client {
c.lock.Lock()
defer c.lock.Unlock()
c.retryHooks = append(c.retryHooks, hook)
c.retryHooks = append(c.retryHooks, hooks...)
return c
}

Expand Down
3 changes: 2 additions & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ func TestClientSettingsCoverage(t *testing.T) {
assertEqual(t, time.Millisecond*100, c.RetryWaitTime())
assertEqual(t, time.Second*2, c.RetryMaxWaitTime())
assertEqual(t, false, c.IsTrace())
assertEqual(t, 0, len(c.RetryConditions()))

authToken := "sample auth token value"
c.SetAuthToken(authToken)
Expand Down Expand Up @@ -1144,7 +1145,7 @@ func TestClientOnResponseError(t *testing.T) {
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF").
SetRetryCount(0).
SetRetryMaxWaitTime(time.Microsecond).
AddRetryCondition(func(response *Response, err error) bool {
AddRetryConditions(func(response *Response, err error) bool {
if err != nil {
return true
}
Expand Down
51 changes: 40 additions & 11 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type Request struct {
multipartBoundary string
multipartFields []*MultipartField
retryConditions []RetryConditionFunc
retryHooks []RetryHookFunc
resultCurlCmd string
generateCurlCmd bool
debugLogCurlCmd bool
Expand Down Expand Up @@ -945,14 +946,43 @@ func (r *Request) SetDebug(d bool) *Request {
return r
}

// AddRetryCondition method adds a retry condition function to the request's
// array of functions is checked to determine if the request can be retried.
// The request will retry if any functions return true and the error is nil.
// AddRetryConditions method adds one or more retry condition functions into the request.
// These retry conditions are executed to determine if the request can be retried.
// The request will retry if any functions return `true`, otherwise return `false`.
//
// NOTE: The request level retry conditions are checked before all retry
// conditions from the client instance.
func (r *Request) AddRetryCondition(condition RetryConditionFunc) *Request {
r.retryConditions = append(r.retryConditions, condition)
// NOTE:
// - The client-level retry conditions are applied to all requests.
// - The request-level retry conditions are executed first before client-level
// retry conditions. See [Request.SetRetryConditions]
func (r *Request) AddRetryConditions(conditions ...RetryConditionFunc) *Request {
r.retryConditions = append(r.retryConditions, conditions...)
return r
}

// SetRetryConditions method overwrites the retry conditions in the request.
// These retry conditions are executed to determine if the request can be retried.
// The request will retry if any function returns `true`, otherwise return `false`.
func (r *Request) SetRetryConditions(conditions ...RetryConditionFunc) *Request {
r.retryConditions = conditions
return r
}

// AddRetryHooks method adds one or more side-effecting retry hooks in the request.
//
// NOTE:
// - All the retry hooks are executed on each request retry.
// - The request-level retry hooks are executed first before client-level hooks.
func (r *Request) AddRetryHooks(hooks ...RetryHookFunc) *Request {
r.retryHooks = append(r.retryHooks, hooks...)
return r
}

// SetRetryHooks method overwrites side-effecting retry hooks in the request.
//
// NOTE:
// - All the retry hooks are executed on each request retry.
func (r *Request) SetRetryHooks(hooks ...RetryHookFunc) *Request {
r.retryHooks = hooks
return r
}

Expand Down Expand Up @@ -1355,8 +1385,7 @@ func (r *Request) Execute(method, url string) (res *Response, err error) {
// is still false
if !needsRetry && res != nil {
// user defined retry conditions
retryConditions := append(r.retryConditions, r.client.RetryConditions()...)
for _, retryCondition := range retryConditions {
for _, retryCondition := range r.retryConditions {
if needsRetry = retryCondition(res, err); needsRetry {
break
}
Expand All @@ -1375,7 +1404,7 @@ func (r *Request) Execute(method, url string) (res *Response, err error) {
}

// run user-defined retry hooks
for _, retryHookFunc := range r.client.RetryHooks() {
for _, retryHookFunc := range r.retryHooks {
retryHookFunc(res, err)
}

Expand All @@ -1393,11 +1422,11 @@ func (r *Request) Execute(method, url string) (res *Response, err error) {
select {
case <-r.Context().Done():
isCtxDone = true
timer.Stop()
err = wrapErrors(r.Context().Err(), err)
break
case <-timer.C:
}
timer.Stop()
if isCtxDone {
break
}
Expand Down
2 changes: 1 addition & 1 deletion request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2137,7 +2137,7 @@ func TestRequestNoRetryOnNonIdempotentMethod(t *testing.T) {

c := dcnl().
SetTimeout(time.Second * 3).
AddRetryHook(
AddRetryHooks(
func(response *Response, _ error) {
read, err := bufReader.Read(bufCpy)

Expand Down
Loading

0 comments on commit 4311699

Please sign in to comment.