1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00
2020-01-17 20:42:25 +00:00

155 lines
4.7 KiB
Go

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package azuredevops
import (
"context"
"encoding/base64"
"github.com/google/uuid"
"strings"
"sync"
"time"
)
// 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,
}
}
func NewAnonymousConnection(organizationUrl string) *Connection {
organizationUrl = normalizeUrl(organizationUrl)
return &Connection{
BaseUrl: organizationUrl,
SuppressFedAuthRedirect: true,
}
}
type Connection struct {
AuthorizationString string
BaseUrl string
UserAgent string
SuppressFedAuthRedirect bool
ForceMsaPassThrough bool
Timeout *time.Duration
clientCache map[string]Client
clientCacheLock sync.RWMutex
resourceAreaCache map[uuid.UUID]ResourceAreaInfo
resourceAreaCacheLock sync.RWMutex
}
func CreateBasicAuthHeaderValue(username, password string) string {
auth := username + ":" + password
return "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
}
func normalizeUrl(url string) string {
return strings.ToLower(strings.TrimRight(url, "/"))
}
func (connection *Connection) GetClientByResourceAreaId(ctx context.Context, resourceAreaID uuid.UUID) (*Client, error) {
resourceAreaInfo, err := connection.getResourceAreaInfo(ctx, resourceAreaID)
if err != nil {
return nil, err
}
var client *Client
if resourceAreaInfo != nil {
client = connection.GetClientByUrl(*resourceAreaInfo.LocationUrl)
} else {
// resourceAreaInfo will be nil for on prem servers
client = connection.GetClientByUrl(connection.BaseUrl)
}
return client, nil
}
func (connection *Connection) GetClientByUrl(baseUrl string) *Client {
normalizedUrl := normalizeUrl(baseUrl)
azureDevOpsClient, ok := connection.getClientCacheEntry(normalizedUrl)
if !ok {
azureDevOpsClient = NewClient(connection, normalizedUrl)
connection.setClientCacheEntry(normalizedUrl, azureDevOpsClient)
}
return azureDevOpsClient
}
func (connection *Connection) getResourceAreaInfo(ctx context.Context, resourceAreaId uuid.UUID) (*ResourceAreaInfo, error) {
resourceAreaInfo, ok := connection.getResourceAreaCacheEntry(resourceAreaId)
if !ok {
client := connection.GetClientByUrl(connection.BaseUrl)
resourceAreaInfos, err := client.GetResourceAreas(ctx)
if err != nil {
return nil, err
}
if len(*resourceAreaInfos) > 0 {
for _, resourceEntry := range *resourceAreaInfos {
connection.setResourceAreaCacheEntry(*resourceEntry.Id, &resourceEntry)
}
resourceAreaInfo, ok = connection.getResourceAreaCacheEntry(resourceAreaId)
} else {
// on prem servers return an empty list
return nil, nil
}
}
if ok {
return resourceAreaInfo, nil
}
return nil, &ResourceAreaIdNotRegisteredError{resourceAreaId, connection.BaseUrl}
}
// Client Cache by Url
func (connection *Connection) getClientCacheEntry(url string) (*Client, bool) {
if connection.clientCache == nil {
return nil, false
}
connection.clientCacheLock.RLock()
defer connection.clientCacheLock.RUnlock()
client, ok := connection.clientCache[url]
return &client, ok
}
func (connection *Connection) setClientCacheEntry(url string, client *Client) {
connection.clientCacheLock.Lock()
defer connection.clientCacheLock.Unlock()
if connection.clientCache == nil {
connection.clientCache = make(map[string]Client)
}
connection.clientCache[url] = *client
}
func (connection *Connection) getResourceAreaCacheEntry(resourceAreaId uuid.UUID) (*ResourceAreaInfo, bool) {
if connection.resourceAreaCache == nil {
return nil, false
}
connection.resourceAreaCacheLock.RLock()
defer connection.resourceAreaCacheLock.RUnlock()
resourceAreaInfo, ok := connection.resourceAreaCache[resourceAreaId]
return &resourceAreaInfo, ok
}
func (connection *Connection) setResourceAreaCacheEntry(resourceAreaId uuid.UUID, resourceAreaInfo *ResourceAreaInfo) {
connection.resourceAreaCacheLock.Lock()
defer connection.resourceAreaCacheLock.Unlock()
if connection.resourceAreaCache == nil {
connection.resourceAreaCache = make(map[uuid.UUID]ResourceAreaInfo)
}
connection.resourceAreaCache[resourceAreaId] = *resourceAreaInfo
}
type ResourceAreaIdNotRegisteredError struct {
ResourceAreaId uuid.UUID
Url string
}
func (e ResourceAreaIdNotRegisteredError) Error() string {
return "API resource area Id " + e.ResourceAreaId.String() + " is not registered on " + e.Url + "."
}