github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/azure/internal/ad/client.go (about)

     1  // This file is based on code from Azure/azure-sdk-for-go
     2  // and Azure/go-autorest, which are both
     3  // Copyright Microsoft Corporation. See the LICENSE
     4  // file in this directory for details.
     5  //
     6  // NOTE(axw) this file contains a client for a subset of the
     7  // Microsoft Graph API, which is not currently supported by
     8  // the Azure SDK. When it is, this will be deleted.
     9  
    10  package ad
    11  
    12  import (
    13  	"fmt"
    14  	"io/ioutil"
    15  	"net/http"
    16  
    17  	"github.com/Azure/go-autorest/autorest"
    18  	"github.com/Azure/go-autorest/autorest/azure"
    19  
    20  	"github.com/juju/juju/version"
    21  )
    22  
    23  const (
    24  	// APIVersion is the version of the Active Directory API
    25  	APIVersion = "1.6"
    26  )
    27  
    28  func UserAgent() string {
    29  	return "Juju/" + version.Current.String()
    30  }
    31  
    32  type ManagementClient struct {
    33  	autorest.Client
    34  	BaseURI    string
    35  	APIVersion string
    36  }
    37  
    38  func NewManagementClient(baseURI string) ManagementClient {
    39  	return ManagementClient{
    40  		Client:     autorest.NewClientWithUserAgent(UserAgent()),
    41  		BaseURI:    baseURI,
    42  		APIVersion: APIVersion,
    43  	}
    44  }
    45  
    46  // WithOdataErrorUnlessStatusCode returns a RespondDecorator that emits an
    47  // azure.RequestError by reading the response body unless the response HTTP status code
    48  // is among the set passed.
    49  //
    50  // If there is a chance service may return responses other than the Azure error
    51  // format and the response cannot be parsed into an error, a decoding error will
    52  // be returned containing the response body. In any case, the Responder will
    53  // return an error if the status code is not satisfied.
    54  //
    55  // If this Responder returns an error, the response body will be replaced with
    56  // an in-memory reader, which needs no further closing.
    57  //
    58  // NOTE(axw) this function is based on go-autorest/autorest/azure.WithErrorUnlessStatusCode.
    59  // The only difference is that we extract "odata.error", instead of "error",
    60  // from the response body; and we do not extract the message, which is in a
    61  // different format for odata.error, and irrelevant to us.
    62  func WithOdataErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
    63  	return func(r autorest.Responder) autorest.Responder {
    64  		return autorest.ResponderFunc(func(resp *http.Response) error {
    65  			err := r.Respond(resp)
    66  			if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
    67  				var oe odataRequestError
    68  				defer resp.Body.Close()
    69  
    70  				// Copy and replace the Body in case it does not contain an error object.
    71  				// This will leave the Body available to the caller.
    72  				b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &oe)
    73  				resp.Body = ioutil.NopCloser(&b)
    74  				if decodeErr != nil {
    75  					return fmt.Errorf("ad: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
    76  				} else if oe.ServiceError == nil {
    77  					oe.ServiceError = &odataServiceError{Code: "Unknown"}
    78  				}
    79  
    80  				e := azure.RequestError{
    81  					ServiceError: &azure.ServiceError{Code: oe.ServiceError.Code},
    82  					RequestID:    azure.ExtractRequestID(resp),
    83  				}
    84  				if e.StatusCode == nil {
    85  					e.StatusCode = resp.StatusCode
    86  				}
    87  				err = &e
    88  			}
    89  			return err
    90  		})
    91  	}
    92  }
    93  
    94  type odataRequestError struct {
    95  	ServiceError *odataServiceError `json:"odata.error"`
    96  }
    97  
    98  type odataServiceError struct {
    99  	Code string `json:"code"`
   100  }