From 7b847ebfe52242115d3d4b0b34e950ed2a236eb4 Mon Sep 17 00:00:00 2001 From: Prashanth Balasubramanian Date: Fri, 8 Jan 2016 18:51:50 -0800 Subject: [PATCH] Add api/gensupport as a godep --- Godeps/Godeps.json | 4 + .../google.golang.org/api/gensupport/doc.go | 10 + .../google.golang.org/api/gensupport/json.go | 172 ++++++++++++++++++ .../google.golang.org/api/gensupport/media.go | 164 +++++++++++++++++ .../api/gensupport/params.go | 31 ++++ 5 files changed, 381 insertions(+) create mode 100644 Godeps/_workspace/src/google.golang.org/api/gensupport/doc.go create mode 100644 Godeps/_workspace/src/google.golang.org/api/gensupport/json.go create mode 100644 Godeps/_workspace/src/google.golang.org/api/gensupport/media.go create mode 100644 Godeps/_workspace/src/google.golang.org/api/gensupport/params.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 2ccfc160101..e802212b291 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -935,6 +935,10 @@ "ImportPath": "google.golang.org/api/container/v1", "Rev": "77e7d383beb96054547729f49c372b3d01e196ff" }, + { + "ImportPath": "google.golang.org/api/gensupport", + "Rev": "77e7d383beb96054547729f49c372b3d01e196ff" + }, { "ImportPath": "google.golang.org/api/googleapi", "Rev": "77e7d383beb96054547729f49c372b3d01e196ff" diff --git a/Godeps/_workspace/src/google.golang.org/api/gensupport/doc.go b/Godeps/_workspace/src/google.golang.org/api/gensupport/doc.go new file mode 100644 index 00000000000..752c4b411b2 --- /dev/null +++ b/Godeps/_workspace/src/google.golang.org/api/gensupport/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gensupport is an internal implementation detail used by code +// generated by the google-api-go-generator tool. +// +// This package may be modified at any time without regard for backwards +// compatibility. It should not be used directly by API users. +package gensupport diff --git a/Godeps/_workspace/src/google.golang.org/api/gensupport/json.go b/Godeps/_workspace/src/google.golang.org/api/gensupport/json.go new file mode 100644 index 00000000000..dd7bcd2eb07 --- /dev/null +++ b/Godeps/_workspace/src/google.golang.org/api/gensupport/json.go @@ -0,0 +1,172 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gensupport + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSON returns a JSON encoding of schema containing only selected fields. +// A field is selected if: +// * it has a non-empty value, or +// * its field name is present in forceSendFields, and +// * it is not a nil pointer or nil interface. +// The JSON key for each selected field is taken from the field's json: struct tag. +func MarshalJSON(schema interface{}, forceSendFields []string) ([]byte, error) { + if len(forceSendFields) == 0 { + return json.Marshal(schema) + } + + mustInclude := make(map[string]struct{}) + for _, f := range forceSendFields { + mustInclude[f] = struct{}{} + } + + dataMap, err := schemaToMap(schema, mustInclude) + if err != nil { + return nil, err + } + return json.Marshal(dataMap) +} + +func schemaToMap(schema interface{}, mustInclude map[string]struct{}) (map[string]interface{}, error) { + m := make(map[string]interface{}) + s := reflect.ValueOf(schema) + st := s.Type() + + for i := 0; i < s.NumField(); i++ { + jsonTag := st.Field(i).Tag.Get("json") + if jsonTag == "" { + continue + } + tag, err := parseJSONTag(jsonTag) + if err != nil { + return nil, err + } + if tag.ignore { + continue + } + + v := s.Field(i) + f := st.Field(i) + if !includeField(v, f, mustInclude) { + continue + } + + // nil maps are treated as empty maps. + if f.Type.Kind() == reflect.Map && v.IsNil() { + m[tag.apiName] = map[string]string{} + continue + } + + // nil slices are treated as empty slices. + if f.Type.Kind() == reflect.Slice && v.IsNil() { + m[tag.apiName] = []bool{} + continue + } + + if tag.stringFormat { + m[tag.apiName] = formatAsString(v, f.Type.Kind()) + } else { + m[tag.apiName] = v.Interface() + } + } + return m, nil +} + +// formatAsString returns a string representation of v, dereferencing it first if possible. +func formatAsString(v reflect.Value, kind reflect.Kind) string { + if kind == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + + return fmt.Sprintf("%v", v.Interface()) +} + +// jsonTag represents a restricted version of the struct tag format used by encoding/json. +// It is used to describe the JSON encoding of fields in a Schema struct. +type jsonTag struct { + apiName string + stringFormat bool + ignore bool +} + +// parseJSONTag parses a restricted version of the struct tag format used by encoding/json. +// The format of the tag must match that generated by the Schema.writeSchemaStruct method +// in the api generator. +func parseJSONTag(val string) (jsonTag, error) { + if val == "-" { + return jsonTag{ignore: true}, nil + } + + var tag jsonTag + + i := strings.Index(val, ",") + if i == -1 || val[:i] == "" { + return tag, fmt.Errorf("malformed json tag: %s", val) + } + + tag = jsonTag{ + apiName: val[:i], + } + + switch val[i+1:] { + case "omitempty": + case "omitempty,string": + tag.stringFormat = true + default: + return tag, fmt.Errorf("malformed json tag: %s", val) + } + + return tag, nil +} + +// Reports whether the struct field "f" with value "v" should be included in JSON output. +func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]struct{}) bool { + // The regular JSON encoding of a nil pointer is "null", which means "delete this field". + // Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set. + // However, many fields are not pointers, so there would be no way to delete these fields. + // Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields. + // Deletion will be handled by a separate mechanism. + if f.Type.Kind() == reflect.Ptr && v.IsNil() { + return false + } + + // The "any" type is represented as an interface{}. If this interface + // is nil, there is no reasonable representation to send. We ignore + // these fields, for the same reasons as given above for pointers. + if f.Type.Kind() == reflect.Interface && v.IsNil() { + return false + } + + _, ok := mustInclude[f.Name] + return ok || !isEmptyValue(v) +} + +// isEmptyValue reports whether v is the empty value for its type. This +// implementation is based on that of the encoding/json package, but its +// correctness does not depend on it being identical. What's important is that +// this function return false in situations where v should not be sent as part +// of a PATCH operation. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} diff --git a/Godeps/_workspace/src/google.golang.org/api/gensupport/media.go b/Godeps/_workspace/src/google.golang.org/api/gensupport/media.go new file mode 100644 index 00000000000..d1d448d32c8 --- /dev/null +++ b/Godeps/_workspace/src/google.golang.org/api/gensupport/media.go @@ -0,0 +1,164 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gensupport + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/textproto" + + "google.golang.org/api/googleapi" +) + +const sniffBuffSize = 512 + +func NewContentSniffer(r io.Reader) *ContentSniffer { + return &ContentSniffer{r: r} +} + +// ContentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader. +type ContentSniffer struct { + r io.Reader + start []byte // buffer for the sniffed bytes. + err error // set to any error encountered while reading bytes to be sniffed. + + ctype string // set on first sniff. + sniffed bool // set to true on first sniff. +} + +func (sct *ContentSniffer) Read(p []byte) (n int, err error) { + // Ensure that the content type is sniffed before any data is consumed from Reader. + _, _ = sct.ContentType() + + if len(sct.start) > 0 { + n := copy(p, sct.start) + sct.start = sct.start[n:] + return n, nil + } + + // We may have read some bytes into start while sniffing, even if the read ended in an error. + // We should first return those bytes, then the error. + if sct.err != nil { + return 0, sct.err + } + + // Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader. + return sct.r.Read(p) +} + +// ContentType returns the sniffed content type, and whether the content type was succesfully sniffed. +func (sct *ContentSniffer) ContentType() (string, bool) { + if sct.sniffed { + return sct.ctype, sct.ctype != "" + } + sct.sniffed = true + // If ReadAll hits EOF, it returns err==nil. + sct.start, sct.err = ioutil.ReadAll(io.LimitReader(sct.r, sniffBuffSize)) + + // Don't try to detect the content type based on possibly incomplete data. + if sct.err != nil { + return "", false + } + + sct.ctype = http.DetectContentType(sct.start) + return sct.ctype, true +} + +// IncludeMedia combines an existing HTTP body with media content to create a multipart/related HTTP body. +// +// bodyp is an in/out parameter. It should initially point to the +// reader of the application/json (or whatever) payload to send in the +// API request. It's updated to point to the multipart body reader. +// +// ctypep is an in/out parameter. It should initially point to the +// content type of the bodyp, usually "application/json". It's updated +// to the "multipart/related" content type, with random boundary. +// +// The return value is a function that can be used to close the bodyp Reader with an error. +func IncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) func() { + var mediaType string + media, mediaType = getMediaType(media) + + body, bodyType := *bodyp, *ctypep + + pr, pw := io.Pipe() + mpw := multipart.NewWriter(pw) + *bodyp = pr + *ctypep = "multipart/related; boundary=" + mpw.Boundary() + go func() { + w, err := mpw.CreatePart(typeHeader(bodyType)) + if err != nil { + mpw.Close() + pw.CloseWithError(fmt.Errorf("googleapi: body CreatePart failed: %v", err)) + return + } + _, err = io.Copy(w, body) + if err != nil { + mpw.Close() + pw.CloseWithError(fmt.Errorf("googleapi: body Copy failed: %v", err)) + return + } + + w, err = mpw.CreatePart(typeHeader(mediaType)) + if err != nil { + mpw.Close() + pw.CloseWithError(fmt.Errorf("googleapi: media CreatePart failed: %v", err)) + return + } + _, err = io.Copy(w, media) + if err != nil { + mpw.Close() + pw.CloseWithError(fmt.Errorf("googleapi: media Copy failed: %v", err)) + return + } + mpw.Close() + pw.Close() + }() + return func() { pw.CloseWithError(errAborted) } +} + +var errAborted = errors.New("googleapi: upload aborted") + +func getMediaType(media io.Reader) (io.Reader, string) { + if typer, ok := media.(googleapi.ContentTyper); ok { + return media, typer.ContentType() + } + + sniffer := NewContentSniffer(media) + typ, ok := sniffer.ContentType() + if !ok { + // TODO(mcgreevy): Remove this default. It maintains the semantics of the existing code, + // but should not be relied on. + typ = "application/octet-stream" + } + return sniffer, typ +} + +// DetectMediaType detects and returns the content type of the provided media. +// If the type can not be determined, "application/octet-stream" is returned. +func DetectMediaType(media io.ReaderAt) string { + if typer, ok := media.(googleapi.ContentTyper); ok { + return typer.ContentType() + } + + typ := "application/octet-stream" + buf := make([]byte, 1024) + n, err := media.ReadAt(buf, 0) + buf = buf[:n] + if err == nil || err == io.EOF { + typ = http.DetectContentType(buf) + } + return typ +} + +func typeHeader(contentType string) textproto.MIMEHeader { + h := make(textproto.MIMEHeader) + h.Set("Content-Type", contentType) + return h +} diff --git a/Godeps/_workspace/src/google.golang.org/api/gensupport/params.go b/Godeps/_workspace/src/google.golang.org/api/gensupport/params.go new file mode 100644 index 00000000000..dfad3f414d0 --- /dev/null +++ b/Godeps/_workspace/src/google.golang.org/api/gensupport/params.go @@ -0,0 +1,31 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gensupport + +import "net/url" + +// URLParams is a simplified replacement for url.Values +// that safely builds up URL parameters for encoding. +type URLParams map[string][]string + +// Set sets the key to value. +// It replaces any existing values. +func (u URLParams) Set(key, value string) { + u[key] = []string{value} +} + +// SetMulti sets the key to an array of values. +// It replaces any existing values. +// Note that values must not be modified after calling SetMulti +// so the caller is responsible for making a copy if necessary. +func (u URLParams) SetMulti(key string, values []string) { + u[key] = values +} + +// Encode encodes the values into ``URL encoded'' form +// ("bar=baz&foo=quux") sorted by key. +func (u URLParams) Encode() string { + return url.Values(u).Encode() +}