Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable specifying SendDecorators on a client #506

Merged
merged 2 commits into from
Feb 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG

## v13.4.0

## New Features

- Added field `SendDecorators` to the `Client` type. This can be used to specify a custom chain of SendDecorators per client.
- Added method `Client.Send()` which includes logic for selecting the preferred chain of SendDecorators.

## v13.3.3

### Bug Fixes
Expand Down
23 changes: 23 additions & 0 deletions autorest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ type Client struct {

// Set to true to skip attempted registration of resource providers (false by default).
SkipResourceProviderRegistration bool

// SendDecorators can be used to override the default chain of SendDecorators.
// This can be used to specify things like a custom retry SendDecorator.
// Set this to an empty slice to use no SendDecorators.
SendDecorators []SendDecorator
}

// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
Expand Down Expand Up @@ -298,3 +303,21 @@ func (c Client) ByInspecting() RespondDecorator {
}
return c.ResponseInspector
}

// Send sends the provided http.Request using the client's Sender or the default sender.
// It returns the http.Response and possible error. It also accepts a, possibly empty,
// default set of SendDecorators used when sending the request.
// SendDecorators have the following precedence:
// 1. In a request's context via WithSendDecorators()
// 2. Specified on the client in SendDecorators
// 3. The default values specified in this method
func (c Client) Send(req *http.Request, decorators ...SendDecorator) (*http.Response, error) {
if c.SendDecorators != nil {
decorators = c.SendDecorators
}
inCtx := req.Context().Value(ctxSendDecorators{})
if sd, ok := inCtx.([]SendDecorator); ok {
decorators = sd
}
return SendWithSender(c, req, decorators...)
}
90 changes: 90 additions & 0 deletions autorest/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package autorest

import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -461,3 +462,92 @@ func randomString(n int) string {
}
return string(s)
}

func TestClientSendMethod(t *testing.T) {
sender := mocks.NewSender()
sender.AppendResponse(newAcceptedResponse())
client := Client{
Sender: sender,
}
req, err := http.NewRequest(http.MethodGet, mocks.TestURL, nil)
req = req.WithContext(context.Background())
if err != nil {
t.Fatal(err)
}
// no SendDecorators
resp, err := client.Send(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusAccepted {
t.Fatalf("expected status code %d, got %d", http.StatusAccepted, resp.StatusCode)
}
// default SendDecorators
sender.AppendResponse(newAcceptedResponse())
resp, err = client.Send(req, DefaultSendDecorator())
if err != nil {
t.Fatal(err)
}
if v := resp.Header.Get("default-decorator"); v != "true" {
t.Fatal("didn't find default-decorator header in response")
}
// using client SendDecorators
sender.AppendResponse(newAcceptedResponse())
client.SendDecorators = []SendDecorator{ClientSendDecorator()}
resp, err = client.Send(req, DefaultSendDecorator())
if err != nil {
t.Fatal(err)
}
if v := resp.Header.Get("client-decorator"); v != "true" {
t.Fatal("didn't find client-decorator header in response")
}
if v := resp.Header.Get("default-decorator"); v == "true" {
t.Fatal("unexpected default-decorator header in response")
}
// using context SendDecorators
sender.AppendResponse(newAcceptedResponse())
req = req.WithContext(WithSendDecorators(req.Context(), []SendDecorator{ContextSendDecorator()}))
resp, err = client.Send(req, DefaultSendDecorator())
if err != nil {
t.Fatal(err)
}
if v := resp.Header.Get("context-decorator"); v != "true" {
t.Fatal("didn't find context-decorator header in response")
}
if v := resp.Header.Get("client-decorator"); v == "true" {
t.Fatal("unexpected client-decorator header in response")
}
if v := resp.Header.Get("default-decorator"); v == "true" {
t.Fatal("unexpected default-decorator header in response")
}
}

func DefaultSendDecorator() SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) {
resp, err := s.Do(r)
resp.Header.Set("default-decorator", "true")
return resp, err
})
}
}

func ClientSendDecorator() SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) {
resp, err := s.Do(r)
resp.Header.Set("client-decorator", "true")
return resp, err
})
}
}

func ContextSendDecorator() SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) {
resp, err := s.Do(r)
resp.Header.Set("context-decorator", "true")
return resp, err
})
}
}
2 changes: 1 addition & 1 deletion autorest/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"runtime"
)

const number = "v13.3.3"
const number = "v13.4.0"

var (
userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
Expand Down