aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go
blob: 3f4d13421aacda48ba52ab11635d10862d213de0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
Package azure provides Azure-specific implementations used with AutoRest.

See the included examples for more detail.
*/
package azure

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strconv"

    "github.com/Azure/go-autorest/autorest"
)

const (
    // HeaderClientID is the Azure extension header to set a user-specified request ID.
    HeaderClientID = "x-ms-client-request-id"

    // HeaderReturnClientID is the Azure extension header to set if the user-specified request ID
    // should be included in the response.
    HeaderReturnClientID = "x-ms-return-client-request-id"

    // HeaderRequestID is the Azure extension header of the service generated request ID returned
    // in the response.
    HeaderRequestID = "x-ms-request-id"
)

// ServiceError encapsulates the error response from an Azure service.
type ServiceError struct {
    Code    string         `json:"code"`
    Message string         `json:"message"`
    Details *[]interface{} `json:"details"`
}

func (se ServiceError) Error() string {
    if se.Details != nil {
        d, err := json.Marshal(*(se.Details))
        if err != nil {
            return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, *se.Details)
        }
        return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, string(d))
    }
    return fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message)
}

// RequestError describes an error response returned by Azure service.
type RequestError struct {
    autorest.DetailedError

    // The error returned by the Azure service.
    ServiceError *ServiceError `json:"error"`

    // The request id (from the x-ms-request-id-header) of the request.
    RequestID string
}

// Error returns a human-friendly error message from service error.
func (e RequestError) Error() string {
    return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v",
        e.StatusCode, e.ServiceError)
}

// IsAzureError returns true if the passed error is an Azure Service error; false otherwise.
func IsAzureError(e error) bool {
    _, ok := e.(*RequestError)
    return ok
}

// NewErrorWithError creates a new Error conforming object from the
// passed packageType, method, statusCode of the given resp (UndefinedStatusCode
// if resp is nil), message, and original error. message is treated as a format
// string to which the optional args apply.
func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError {
    if v, ok := original.(*RequestError); ok {
        return *v
    }

    statusCode := autorest.UndefinedStatusCode
    if resp != nil {
        statusCode = resp.StatusCode
    }
    return RequestError{
        DetailedError: autorest.DetailedError{
            Original:    original,
            PackageType: packageType,
            Method:      method,
            StatusCode:  statusCode,
            Message:     fmt.Sprintf(message, args...),
        },
    }
}

// WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of
// x-ms-client-request-id whose value is the passed, undecorated UUID (e.g.,
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id
// header to true such that UUID accompanies the http.Response.
func WithReturningClientID(uuid string) autorest.PrepareDecorator {
    preparer := autorest.CreatePreparer(
        WithClientID(uuid),
        WithReturnClientID(true))

    return func(p autorest.Preparer) autorest.Preparer {
        return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
            r, err := p.Prepare(r)
            if err != nil {
                return r, err
            }
            return preparer.Prepare(r)
        })
    }
}

// WithClientID returns a PrepareDecorator that adds an HTTP extension header of
// x-ms-client-request-id whose value is passed, undecorated UUID (e.g.,
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA").
func WithClientID(uuid string) autorest.PrepareDecorator {
    return autorest.WithHeader(HeaderClientID, uuid)
}

// WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of
// x-ms-return-client-request-id whose boolean value indicates if the value of the
// x-ms-client-request-id header should be included in the http.Response.
func WithReturnClientID(b bool) autorest.PrepareDecorator {
    return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b))
}

// ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the
// http.Request sent to the service (and returned in the http.Response)
func ExtractClientID(resp *http.Response) string {
    return autorest.ExtractHeaderValue(HeaderClientID, resp)
}

// ExtractRequestID extracts the Azure server generated request identifier from the
// x-ms-request-id header.
func ExtractRequestID(resp *http.Response) string {
    return autorest.ExtractHeaderValue(HeaderRequestID, resp)
}

// WithErrorUnlessStatusCode returns a RespondDecorator that emits an
// azure.RequestError by reading the response body unless the response HTTP status code
// is among the set passed.
//
// If there is a chance service may return responses other than the Azure error
// format and the response cannot be parsed into an error, a decoding error will
// be returned containing the response body. In any case, the Responder will
// return an error if the status code is not satisfied.
//
// If this Responder returns an error, the response body will be replaced with
// an in-memory reader, which needs no further closing.
func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
    return func(r autorest.Responder) autorest.Responder {
        return autorest.ResponderFunc(func(resp *http.Response) error {
            err := r.Respond(resp)
            if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
                var e RequestError
                defer resp.Body.Close()

                // Copy and replace the Body in case it does not contain an error object.
                // This will leave the Body available to the caller.
                b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e)
                resp.Body = ioutil.NopCloser(&b)
                if decodeErr != nil {
                    return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
                } else if e.ServiceError == nil {
                    e.ServiceError = &ServiceError{Code: "Unknown", Message: "Unknown service error"}
                }

                e.RequestID = ExtractRequestID(resp)
                if e.StatusCode == nil {
                    e.StatusCode = resp.StatusCode
                }
                err = &e
            }
            return err
        })
    }
}