diff --git a/azuredevops/client.go b/azuredevops/client.go index 921af58c..46a9dd38 100644 --- a/azuredevops/client.go +++ b/azuredevops/client.go @@ -53,12 +53,17 @@ var baseUserAgent = "go/" + runtime.Version() + " (" + runtime.GOOS + " " + runt // NewClient provides an Azure DevOps client // and copies the TLS config and timeout from the supplied connection func NewClient(connection *Connection, baseUrl string) *Client { - httpClient := &http.Client{} - if connection.TlsConfig != nil { - httpClient.Transport = &http.Transport{TLSClientConfig: connection.TlsConfig} - } - if connection.Timeout != nil { - httpClient.Timeout = *connection.Timeout + var httpClient *http.Client + if connection.httpClient != nil { + httpClient = connection.httpClient + } else { + httpClient = &http.Client{} + if connection.TlsConfig != nil { + httpClient.Transport = &http.Transport{TLSClientConfig: connection.TlsConfig} + } + if connection.Timeout != nil { + httpClient.Timeout = *connection.Timeout + } } return NewClientWithOptions(connection, baseUrl, WithHTTPClient(httpClient)) diff --git a/azuredevops/connection.go b/azuredevops/connection.go index eb76f53c..7fed209a 100644 --- a/azuredevops/connection.go +++ b/azuredevops/connection.go @@ -7,6 +7,7 @@ import ( "context" "crypto/tls" "encoding/base64" + "net/http" "strings" "sync" "time" @@ -16,21 +17,29 @@ import ( // Creates a new Azure DevOps connection instance using a personal access token. func NewPatConnection(organizationUrl string, personalAccessToken string) *Connection { - authorizationString := CreateBasicAuthHeaderValue("", personalAccessToken) - organizationUrl = normalizeUrl(organizationUrl) - return &Connection{ - AuthorizationString: authorizationString, - BaseUrl: organizationUrl, - SuppressFedAuthRedirect: true, - } + return NewConnectionWithOptions(organizationUrl, WithConnectionPersonalAccessToken(personalAccessToken)) } +// NewAnonymousConnection creates a new connection without specifying any authentication. This +// is equivalent to calling NewConnectionWithOptions without specifying any options. func NewAnonymousConnection(organizationUrl string) *Connection { + return NewConnectionWithOptions(organizationUrl) +} + +// NewConnectionWithOptions creates a new connection using the specified options. See WithConnection*() functions. +func NewConnectionWithOptions(organizationUrl string, options ...ConnectionOptionFunc) *Connection { organizationUrl = normalizeUrl(organizationUrl) - return &Connection{ + connection := &Connection{ BaseUrl: organizationUrl, SuppressFedAuthRedirect: true, + httpClient: &http.Client{}, + } + + for _, fn := range options { + fn(connection) } + + return connection } type Connection struct { @@ -45,6 +54,7 @@ type Connection struct { clientCacheLock sync.RWMutex resourceAreaCache map[uuid.UUID]ResourceAreaInfo resourceAreaCacheLock sync.RWMutex + httpClient *http.Client } func CreateBasicAuthHeaderValue(username, password string) string { diff --git a/azuredevops/connection_options.go b/azuredevops/connection_options.go new file mode 100644 index 00000000..74ab8866 --- /dev/null +++ b/azuredevops/connection_options.go @@ -0,0 +1,20 @@ +package azuredevops + +import "net/http" + +type ConnectionOptionFunc func(*Connection) + +// WithConnectionHTTPClient sets the HTTP client that will be used by the connection. +func WithConnectionHTTPClient(client *http.Client) ConnectionOptionFunc { + return func(c *Connection) { + c.httpClient = client + } +} + +// WithConnectionPersonalAccessToken specifies the PAT to use for authentication with Azure DevOps. +func WithConnectionPersonalAccessToken(personalAccessToken string) ConnectionOptionFunc { + return func(c *Connection) { + authorizationString := CreateBasicAuthHeaderValue("", personalAccessToken) + c.AuthorizationString = authorizationString + } +}