Skip to content

Commit

Permalink
Add options bag to PollUntilDone method (#17693)
Browse files Browse the repository at this point in the history
* Add options bag to PollUntilDone method

Passing nil/zero-value options defaults to 30s polling frequency.

* don't mutate caller's options

* update changelog
  • Loading branch information
jhendrixMSFT authored Apr 22, 2022
1 parent d2f6ad6 commit 33fa99a
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 26 deletions.
1 change: 1 addition & 0 deletions sdk/azcore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Renamed `cloud.Configuration.LoginEndpoint` to `.ActiveDirectoryAuthorityHost`
* Removed `AuxiliaryTenants` field from `arm/ClientOptions` and `arm/policy/BearerTokenOptions`
* Removed `TokenRequestOptions.TenantID`
* `Poller[T].PollUntilDone()` now takes an `options *PollUntilDoneOptions` param instead of `freq time.Duration`

### Bugs Fixed

Expand Down
22 changes: 19 additions & 3 deletions sdk/azcore/arm/runtime/poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,36 @@ func NewPollerFromResumeToken[T any](token string, pl runtime.Pipeline, options
}, nil
}

// PollUntilDoneOptions contains the optional values for the Poller[T].PollUntilDone() method.
type PollUntilDoneOptions struct {
// Frequency is the time to wait between polling intervals in absence of a Retry-After header. Allowed minimum is one second.
// Pass zero to accept the default value (30s).
Frequency time.Duration
}

// Poller encapsulates a long-running operation, providing polling facilities until the operation reaches a terminal state.
type Poller[T any] struct {
pt *pollers.Poller
rt *T
}

// PollUntilDone will poll the service endpoint until a terminal state is reached, an error is received, or the context expires.
// freq: the time to wait between intervals in absence of a Retry-After header. Allowed minimum is one second.
func (p *Poller[T]) PollUntilDone(ctx context.Context, freq time.Duration) (T, error) {
// options: pass nil to accept the default values.
// NOTE: the default polling frequency is 30 seconds which works well for most services. However, some services might
// benefit from a shorter or longer duration.
func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOptions) (T, error) {
if options == nil {
options = &PollUntilDoneOptions{}
}
cp := *options
if cp.Frequency == 0 {
cp.Frequency = 30 * time.Second
}
var resp T
if p.rt != nil {
resp = *p.rt
}
_, err := p.pt.PollUntilDone(ctx, freq, &resp)
_, err := p.pt.PollUntilDone(ctx, cp.Frequency, &resp)
return resp, err
}

Expand Down
12 changes: 6 additions & 6 deletions sdk/azcore/arm/runtime/poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func TestNewPollerAsync(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := poller.PollUntilDone(context.Background(), time.Second)
result, err := poller.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -128,7 +128,7 @@ func TestNewPollerBody(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := poller.PollUntilDone(context.Background(), time.Second)
result, err := poller.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -203,7 +203,7 @@ func TestNewPollerInitialRetryAfter(t *testing.T) {
if pt := pollers.PollerType(poller.pt); pt != reflect.TypeOf(&async.Poller{}) {
t.Fatalf("unexpected poller type %s", pt.String())
}
result, err := poller.PollUntilDone(context.Background(), time.Second)
result, err := poller.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -266,7 +266,7 @@ func TestNewPollerFailedWithError(t *testing.T) {
if pt := pollers.PollerType(poller.pt); pt != reflect.TypeOf(&async.Poller{}) {
t.Fatalf("unexpected poller type %s", pt.String())
}
_, err = poller.PollUntilDone(context.Background(), time.Second)
_, err = poller.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err == nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -301,7 +301,7 @@ func TestNewPollerSuccessNoContent(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := poller.PollUntilDone(context.Background(), time.Second)
result, err := poller.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -363,7 +363,7 @@ func TestNewPollerWithResponseType(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := poller.PollUntilDone(context.Background(), time.Second)
result, err := poller.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down
22 changes: 19 additions & 3 deletions sdk/azcore/runtime/poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,36 @@ func NewPollerFromResumeToken[T any](token string, pl exported.Pipeline, options
}, nil
}

// PollUntilDoneOptions contains the optional values for the Poller[T].PollUntilDone() method.
type PollUntilDoneOptions struct {
// Frequency is the time to wait between polling intervals in absence of a Retry-After header. Allowed minimum is one second.
// Pass zero to accept the default value (30s).
Frequency time.Duration
}

// Poller encapsulates a long-running operation, providing polling facilities until the operation reaches a terminal state.
type Poller[T any] struct {
pt *pollers.Poller
rt *T
}

// PollUntilDone will poll the service endpoint until a terminal state is reached, an error is received, or the context expires.
// freq: the time to wait between intervals in absence of a Retry-After header. Allowed minimum is one second.
func (p *Poller[T]) PollUntilDone(ctx context.Context, freq time.Duration) (T, error) {
// options: pass nil to accept the default values.
// NOTE: the default polling frequency is 30 seconds which works well for most services. However, some services might
// benefit from a shorter or longer duration.
func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOptions) (T, error) {
if options == nil {
options = &PollUntilDoneOptions{}
}
cp := *options
if cp.Frequency == 0 {
cp.Frequency = 30 * time.Second
}
var resp T
if p.rt != nil {
resp = *p.rt
}
_, err := p.pt.PollUntilDone(ctx, freq, &resp)
_, err := p.pt.PollUntilDone(ctx, cp.Frequency, &resp)
return resp, err
}

Expand Down
28 changes: 14 additions & 14 deletions sdk/azcore/runtime/poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func TestLocPollerSimple(t *testing.T) {
}
var respFromCtx *http.Response
ctxWithResp := WithCaptureResponse(context.Background(), &respFromCtx)
_, err = lro.PollUntilDone(ctxWithResp, time.Second)
_, err = lro.PollUntilDone(ctxWithResp, &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -129,7 +129,7 @@ func TestLocPollerWithWidget(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -162,7 +162,7 @@ func TestLocPollerCancelled(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err == nil {
t.Fatal("unexpected nil error")
}
Expand Down Expand Up @@ -198,7 +198,7 @@ func TestLocPollerWithError(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err == nil {
t.Fatal("unexpected nil error")
}
Expand Down Expand Up @@ -256,7 +256,7 @@ func TestLocPollerWithResumeToken(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, err = lro.PollUntilDone(context.Background(), time.Second)
_, err = lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -286,7 +286,7 @@ func TestLocPollerWithTimeout(t *testing.T) {
t.Fatal("initial response body wasn't closed")
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
_, err = lro.PollUntilDone(ctx, time.Second)
_, err = lro.PollUntilDone(ctx, &PollUntilDoneOptions{Frequency: time.Second})
cancel()
if err == nil {
t.Fatal("unexpected nil error")
Expand Down Expand Up @@ -325,7 +325,7 @@ func TestOpPollerSimple(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
_, err = lro.PollUntilDone(context.Background(), time.Second)
_, err = lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -365,7 +365,7 @@ func TestOpPollerWithWidgetPUT(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -409,7 +409,7 @@ func TestOpPollerWithWidgetPOSTLocation(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -451,7 +451,7 @@ func TestOpPollerWithWidgetPOST(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -496,7 +496,7 @@ func TestOpPollerWithWidgetResourceLocation(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -559,7 +559,7 @@ func TestOpPollerWithResumeToken(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, err = lro.PollUntilDone(context.Background(), time.Second)
_, err = lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -592,7 +592,7 @@ func TestNopPoller(t *testing.T) {
if resp != firstResp {
t.Fatal("unexpected response")
}
_, err = lro.PollUntilDone(context.Background(), time.Second)
_, err = lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -648,7 +648,7 @@ func TestOpPollerWithResponseType(t *testing.T) {
if !closed() {
t.Fatal("initial response body wasn't closed")
}
w, err := lro.PollUntilDone(context.Background(), time.Second)
w, err := lro.PollUntilDone(context.Background(), &PollUntilDoneOptions{Frequency: time.Second})
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 33fa99a

Please sign in to comment.