| package autorest |
| |
| // Copyright 2017 Microsoft Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "encoding/xml" |
| "fmt" |
| "io" |
| "net" |
| "net/http" |
| "net/url" |
| "reflect" |
| "strings" |
| |
| "github.com/Azure/go-autorest/autorest/adal" |
| ) |
| |
| // EncodedAs is a series of constants specifying various data encodings |
| type EncodedAs string |
| |
| const ( |
| // EncodedAsJSON states that data is encoded as JSON |
| EncodedAsJSON EncodedAs = "JSON" |
| |
| // EncodedAsXML states that data is encoded as Xml |
| EncodedAsXML EncodedAs = "XML" |
| ) |
| |
| // Decoder defines the decoding method json.Decoder and xml.Decoder share |
| type Decoder interface { |
| Decode(v interface{}) error |
| } |
| |
| // NewDecoder creates a new decoder appropriate to the passed encoding. |
| // encodedAs specifies the type of encoding and r supplies the io.Reader containing the |
| // encoded data. |
| func NewDecoder(encodedAs EncodedAs, r io.Reader) Decoder { |
| if encodedAs == EncodedAsJSON { |
| return json.NewDecoder(r) |
| } else if encodedAs == EncodedAsXML { |
| return xml.NewDecoder(r) |
| } |
| return nil |
| } |
| |
| // CopyAndDecode decodes the data from the passed io.Reader while making a copy. Having a copy |
| // is especially useful if there is a chance the data will fail to decode. |
| // encodedAs specifies the expected encoding, r provides the io.Reader to the data, and v |
| // is the decoding destination. |
| func CopyAndDecode(encodedAs EncodedAs, r io.Reader, v interface{}) (bytes.Buffer, error) { |
| b := bytes.Buffer{} |
| return b, NewDecoder(encodedAs, io.TeeReader(r, &b)).Decode(v) |
| } |
| |
| // TeeReadCloser returns a ReadCloser that writes to w what it reads from rc. |
| // It utilizes io.TeeReader to copy the data read and has the same behavior when reading. |
| // Further, when it is closed, it ensures that rc is closed as well. |
| func TeeReadCloser(rc io.ReadCloser, w io.Writer) io.ReadCloser { |
| return &teeReadCloser{rc, io.TeeReader(rc, w)} |
| } |
| |
| type teeReadCloser struct { |
| rc io.ReadCloser |
| r io.Reader |
| } |
| |
| func (t *teeReadCloser) Read(p []byte) (int, error) { |
| return t.r.Read(p) |
| } |
| |
| func (t *teeReadCloser) Close() error { |
| return t.rc.Close() |
| } |
| |
| func containsInt(ints []int, n int) bool { |
| for _, i := range ints { |
| if i == n { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func escapeValueStrings(m map[string]string) map[string]string { |
| for key, value := range m { |
| m[key] = url.QueryEscape(value) |
| } |
| return m |
| } |
| |
| func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string { |
| mapOfStrings := make(map[string]string) |
| for key, value := range mapOfInterface { |
| mapOfStrings[key] = ensureValueString(value) |
| } |
| return mapOfStrings |
| } |
| |
| func ensureValueString(value interface{}) string { |
| if value == nil { |
| return "" |
| } |
| switch v := value.(type) { |
| case string: |
| return v |
| case []byte: |
| return string(v) |
| default: |
| return fmt.Sprintf("%v", v) |
| } |
| } |
| |
| // MapToValues method converts map[string]interface{} to url.Values. |
| func MapToValues(m map[string]interface{}) url.Values { |
| v := url.Values{} |
| for key, value := range m { |
| x := reflect.ValueOf(value) |
| if x.Kind() == reflect.Array || x.Kind() == reflect.Slice { |
| for i := 0; i < x.Len(); i++ { |
| v.Add(key, ensureValueString(x.Index(i))) |
| } |
| } else { |
| v.Add(key, ensureValueString(value)) |
| } |
| } |
| return v |
| } |
| |
| // AsStringSlice method converts interface{} to []string. This expects a |
| //that the parameter passed to be a slice or array of a type that has the underlying |
| //type a string. |
| func AsStringSlice(s interface{}) ([]string, error) { |
| v := reflect.ValueOf(s) |
| if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { |
| return nil, NewError("autorest", "AsStringSlice", "the value's type is not an array.") |
| } |
| stringSlice := make([]string, 0, v.Len()) |
| |
| for i := 0; i < v.Len(); i++ { |
| stringSlice = append(stringSlice, v.Index(i).String()) |
| } |
| return stringSlice, nil |
| } |
| |
| // String method converts interface v to string. If interface is a list, it |
| // joins list elements using the separator. Note that only sep[0] will be used for |
| // joining if any separator is specified. |
| func String(v interface{}, sep ...string) string { |
| if len(sep) == 0 { |
| return ensureValueString(v) |
| } |
| stringSlice, ok := v.([]string) |
| if ok == false { |
| var err error |
| stringSlice, err = AsStringSlice(v) |
| if err != nil { |
| panic(fmt.Sprintf("autorest: Couldn't convert value to a string %s.", err)) |
| } |
| } |
| return ensureValueString(strings.Join(stringSlice, sep[0])) |
| } |
| |
| // Encode method encodes url path and query parameters. |
| func Encode(location string, v interface{}, sep ...string) string { |
| s := String(v, sep...) |
| switch strings.ToLower(location) { |
| case "path": |
| return pathEscape(s) |
| case "query": |
| return queryEscape(s) |
| default: |
| return s |
| } |
| } |
| |
| func pathEscape(s string) string { |
| return strings.Replace(url.QueryEscape(s), "+", "%20", -1) |
| } |
| |
| func queryEscape(s string) string { |
| return url.QueryEscape(s) |
| } |
| |
| // ChangeToGet turns the specified http.Request into a GET (it assumes it wasn't). |
| // This is mainly useful for long-running operations that use the Azure-AsyncOperation |
| // header, so we change the initial PUT into a GET to retrieve the final result. |
| func ChangeToGet(req *http.Request) *http.Request { |
| req.Method = "GET" |
| req.Body = nil |
| req.ContentLength = 0 |
| req.Header.Del("Content-Length") |
| return req |
| } |
| |
| // IsTokenRefreshError returns true if the specified error implements the TokenRefreshError |
| // interface. If err is a DetailedError it will walk the chain of Original errors. |
| func IsTokenRefreshError(err error) bool { |
| if _, ok := err.(adal.TokenRefreshError); ok { |
| return true |
| } |
| if de, ok := err.(DetailedError); ok { |
| return IsTokenRefreshError(de.Original) |
| } |
| return false |
| } |
| |
| // IsTemporaryNetworkError returns true if the specified error is a temporary network error or false |
| // if it's not. If the error doesn't implement the net.Error interface the return value is true. |
| func IsTemporaryNetworkError(err error) bool { |
| if netErr, ok := err.(net.Error); !ok || (ok && netErr.Temporary()) { |
| return true |
| } |
| return false |
| } |