Skip to content

Commit 09a7868

Browse files
author
Marcel S. Henselin
committed
feat: add missing htt StatusCode and make it settable
1 parent 020b00d commit 09a7868

File tree

2 files changed

+62
-19
lines changed

2 files changed

+62
-19
lines changed

core/wait/wait.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/stackitcloud/stackit-sdk-go/core/utils"
1111
)
1212

13-
var RetryHttpErrorStatusCodes = []int{http.StatusBadGateway, http.StatusGatewayTimeout}
13+
var RetryHttpErrorStatusCodes = []int{http.StatusBadGateway, http.StatusGatewayTimeout, http.StatusServiceUnavailable}
1414

1515
// AsyncActionCheck reports whether a specific async action has finished.
1616
// - waitFinished == true if the async action is finished, false otherwise.
@@ -20,25 +20,33 @@ type AsyncActionCheck[T any] func() (waitFinished bool, response *T, err error)
2020

2121
// AsyncActionHandler handles waiting for a specific async action to be finished.
2222
type AsyncActionHandler[T any] struct {
23-
checkFn AsyncActionCheck[T]
24-
sleepBeforeWait time.Duration
25-
throttle time.Duration
26-
timeout time.Duration
27-
tempErrRetryLimit int
28-
IntermediateStateReached bool
23+
checkFn AsyncActionCheck[T]
24+
sleepBeforeWait time.Duration
25+
throttle time.Duration
26+
timeout time.Duration
27+
tempErrRetryLimit int
28+
IntermediateStateReached bool
29+
retryHttpErrorStatusCodes []int
2930
}
3031

3132
// New initializes an AsyncActionHandler
3233
func New[T any](f AsyncActionCheck[T]) *AsyncActionHandler[T] {
3334
return &AsyncActionHandler[T]{
34-
checkFn: f,
35-
sleepBeforeWait: 0 * time.Second,
36-
throttle: 5 * time.Second,
37-
timeout: 30 * time.Minute,
38-
tempErrRetryLimit: 5,
35+
checkFn: f,
36+
sleepBeforeWait: 0 * time.Second,
37+
throttle: 5 * time.Second,
38+
timeout: 30 * time.Minute,
39+
tempErrRetryLimit: 5,
40+
retryHttpErrorStatusCodes: RetryHttpErrorStatusCodes,
3941
}
4042
}
4143

44+
// SetRetryHttpErrorStatusCodes sets the retry codes to use for retry.
45+
func (h *AsyncActionHandler[T]) SetRetryHttpErrorStatusCodes(c []int) *AsyncActionHandler[T] {
46+
h.retryHttpErrorStatusCodes = c
47+
return h
48+
}
49+
4250
// SetThrottle sets the time interval between each check of the async action.
4351
func (h *AsyncActionHandler[T]) SetThrottle(d time.Duration) *AsyncActionHandler[T] {
4452
h.throttle = d
@@ -114,7 +122,7 @@ func (h *AsyncActionHandler[T]) handleError(retryTempErrorCounter int, err error
114122
return retryTempErrorCounter, fmt.Errorf("found non-GenericOpenApiError: %w", err)
115123
}
116124
// Some APIs may return temporary errors and the request should be retried
117-
if !utils.Contains(RetryHttpErrorStatusCodes, oapiErr.StatusCode) {
125+
if !utils.Contains(h.retryHttpErrorStatusCodes, oapiErr.StatusCode) {
118126
return retryTempErrorCounter, err
119127
}
120128
retryTempErrorCounter++

core/wait/wait_test.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ func TestNew(t *testing.T) {
2222
checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil }
2323
got := New(checkFn)
2424
want := &AsyncActionHandler[interface{}]{
25-
checkFn: checkFn,
26-
sleepBeforeWait: 0 * time.Second,
27-
throttle: 5 * time.Second,
28-
timeout: 30 * time.Minute,
29-
tempErrRetryLimit: 5,
25+
checkFn: checkFn,
26+
sleepBeforeWait: 0 * time.Second,
27+
throttle: 5 * time.Second,
28+
timeout: 30 * time.Minute,
29+
tempErrRetryLimit: 5,
30+
retryHttpErrorStatusCodes: RetryHttpErrorStatusCodes,
3031
}
3132

3233
diff := cmp.Diff(got, want, cmpOpts...)
@@ -159,7 +160,41 @@ func TestSetTempErrRetryLimit(t *testing.T) {
159160
got := New(checkFn)
160161
got.SetTempErrRetryLimit(tt.tempErrRetryLimit)
161162

162-
diff := cmp.Diff(got, want, cmpOpts...)
163+
diff := cmp.Diff(want, got, cmpOpts...)
164+
if diff != "" {
165+
t.Errorf("Data does not match: %s", diff)
166+
}
167+
})
168+
}
169+
}
170+
171+
func TestSetRetryHttpErrorStatusCodes(t *testing.T) {
172+
checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil }
173+
174+
for _, tt := range []struct {
175+
desc string
176+
setRetryCodes []int
177+
wantRetryCodes []int
178+
}{
179+
{
180+
"default",
181+
[]int{},
182+
[]int{http.StatusBadGateway, http.StatusGatewayTimeout, http.StatusServiceUnavailable},
183+
},
184+
{
185+
"base_3",
186+
[]int{http.StatusTooManyRequests, http.StatusInternalServerError},
187+
[]int{http.StatusTooManyRequests, http.StatusInternalServerError},
188+
},
189+
} {
190+
t.Run(tt.desc, func(t *testing.T) {
191+
want := New(checkFn)
192+
want.retryHttpErrorStatusCodes = tt.wantRetryCodes
193+
got := New(checkFn)
194+
if len(tt.setRetryCodes) != 0 {
195+
got.SetRetryHttpErrorStatusCodes(tt.setRetryCodes)
196+
}
197+
diff := cmp.Diff(want, got, cmpOpts...)
163198
if diff != "" {
164199
t.Errorf("Data does not match: %s", diff)
165200
}

0 commit comments

Comments
 (0)