Fix GCS
Signed-off-by: Olivier Gambier <olivier@docker.com>
This commit is contained in:
parent
59401e277b
commit
d1444b56e9
141 changed files with 19483 additions and 4205 deletions
383
vendor/google.golang.org/api/googleapi/googleapi.go
generated
vendored
383
vendor/google.golang.org/api/googleapi/googleapi.go
generated
vendored
|
@ -9,21 +9,13 @@ package googleapi
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/api/googleapi/internal/uritemplates"
|
||||
)
|
||||
|
||||
|
@ -42,17 +34,28 @@ type SizeReaderAt interface {
|
|||
Size() int64
|
||||
}
|
||||
|
||||
// ServerResponse is embedded in each Do response and
|
||||
// provides the HTTP status code and header sent by the server.
|
||||
type ServerResponse struct {
|
||||
// HTTPStatusCode is the server's response status code.
|
||||
// When using a resource method's Do call, this will always be in the 2xx range.
|
||||
HTTPStatusCode int
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
const (
|
||||
Version = "0.5"
|
||||
|
||||
// statusResumeIncomplete is the code returned by the Google uploader when the transfer is not yet complete.
|
||||
statusResumeIncomplete = 308
|
||||
|
||||
// UserAgent is the header string used to identify this package.
|
||||
UserAgent = "google-api-go-client/" + Version
|
||||
|
||||
// uploadPause determines the delay between failed upload attempts
|
||||
uploadPause = 1 * time.Second
|
||||
// The default chunk size to use for resumable uplods if not specified by the user.
|
||||
DefaultUploadChunkSize = 8 * 1024 * 1024
|
||||
|
||||
// The minimum chunk size that can be used for resumable uploads. All
|
||||
// user-specified chunk sizes must be multiple of this value.
|
||||
MinUploadChunkSize = 256 * 1024
|
||||
)
|
||||
|
||||
// Error contains an error response from the server.
|
||||
|
@ -65,6 +68,8 @@ type Error struct {
|
|||
// Body is the raw response returned by the server.
|
||||
// It is often but not always JSON, depending on how the request fails.
|
||||
Body string
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
|
||||
Errors []ErrorItem
|
||||
}
|
||||
|
@ -122,6 +127,34 @@ func CheckResponse(res *http.Response) error {
|
|||
return jerr.Error
|
||||
}
|
||||
}
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotModified reports whether err is the result of the
|
||||
// server replying with http.StatusNotModified.
|
||||
// Such error values are sometimes returned by "Do" methods
|
||||
// on calls when If-None-Match is used.
|
||||
func IsNotModified(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ae, ok := err.(*Error)
|
||||
return ok && ae.Code == http.StatusNotModified
|
||||
}
|
||||
|
||||
// CheckMediaResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx. Unlike CheckResponse it does not assume the
|
||||
// body is a JSON error document.
|
||||
func CheckMediaResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
res.Body.Close()
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
|
@ -148,52 +181,6 @@ func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
|
|||
return buf, nil
|
||||
}
|
||||
|
||||
func getMediaType(media io.Reader) (io.Reader, string) {
|
||||
if typer, ok := media.(ContentTyper); ok {
|
||||
return media, typer.ContentType()
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
typ := "application/octet-stream"
|
||||
buf, err := ioutil.ReadAll(io.LimitReader(media, 512))
|
||||
if err != nil {
|
||||
pw.CloseWithError(fmt.Errorf("error reading media: %v", err))
|
||||
return pr, typ
|
||||
}
|
||||
typ = http.DetectContentType(buf)
|
||||
mr := io.MultiReader(bytes.NewReader(buf), media)
|
||||
go func() {
|
||||
_, err = io.Copy(pw, mr)
|
||||
if err != nil {
|
||||
pw.CloseWithError(fmt.Errorf("error reading media: %v", err))
|
||||
return
|
||||
}
|
||||
pw.Close()
|
||||
}()
|
||||
return pr, 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.(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
|
||||
}
|
||||
|
||||
type Lengther interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
// endingWithErrorReader from r until it returns an error. If the
|
||||
// final error from r is io.EOF and e is non-nil, e is used instead.
|
||||
type endingWithErrorReader struct {
|
||||
|
@ -209,12 +196,6 @@ func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func typeHeader(contentType string) textproto.MIMEHeader {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Type", contentType)
|
||||
return h
|
||||
}
|
||||
|
||||
// countingWriter counts the number of bytes it receives to write, but
|
||||
// discards them.
|
||||
type countingWriter struct {
|
||||
|
@ -226,203 +207,65 @@ func (w countingWriter) Write(p []byte) (int, error) {
|
|||
return len(p), nil
|
||||
}
|
||||
|
||||
// ConditionallyIncludeMedia does nothing if media is nil.
|
||||
//
|
||||
// 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 the content-length of the entire multpart body.
|
||||
func ConditionallyIncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) (cancel func(), ok bool) {
|
||||
if media == nil {
|
||||
return
|
||||
}
|
||||
// Get the media type, which might return a different reader instance.
|
||||
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()
|
||||
}()
|
||||
cancel = func() { pw.CloseWithError(errAborted) }
|
||||
return cancel, true
|
||||
}
|
||||
|
||||
var errAborted = errors.New("googleapi: upload aborted")
|
||||
|
||||
// ProgressUpdater is a function that is called upon every progress update of a resumable upload.
|
||||
// This is the only part of a resumable upload (from googleapi) that is usable by the developer.
|
||||
// The remaining usable pieces of resumable uploads is exposed in each auto-generated API.
|
||||
type ProgressUpdater func(current, total int64)
|
||||
|
||||
// ResumableUpload is used by the generated APIs to provide resumable uploads.
|
||||
type MediaOption interface {
|
||||
setOptions(o *MediaOptions)
|
||||
}
|
||||
|
||||
type contentTypeOption string
|
||||
|
||||
func (ct contentTypeOption) setOptions(o *MediaOptions) {
|
||||
o.ContentType = string(ct)
|
||||
if o.ContentType == "" {
|
||||
o.ForceEmptyContentType = true
|
||||
}
|
||||
}
|
||||
|
||||
// ContentType returns a MediaOption which sets the Content-Type header for media uploads.
|
||||
// If ctype is empty, the Content-Type header will be omitted.
|
||||
func ContentType(ctype string) MediaOption {
|
||||
return contentTypeOption(ctype)
|
||||
}
|
||||
|
||||
type chunkSizeOption int
|
||||
|
||||
func (cs chunkSizeOption) setOptions(o *MediaOptions) {
|
||||
size := int(cs)
|
||||
if size%MinUploadChunkSize != 0 {
|
||||
size += MinUploadChunkSize - (size % MinUploadChunkSize)
|
||||
}
|
||||
o.ChunkSize = size
|
||||
}
|
||||
|
||||
// ChunkSize returns a MediaOption which sets the chunk size for media uploads.
|
||||
// size will be rounded up to the nearest multiple of 256K.
|
||||
// Media which contains fewer than size bytes will be uploaded in a single request.
|
||||
// Media which contains size bytes or more will be uploaded in separate chunks.
|
||||
// If size is zero, media will be uploaded in a single request.
|
||||
func ChunkSize(size int) MediaOption {
|
||||
return chunkSizeOption(size)
|
||||
}
|
||||
|
||||
// MediaOptions stores options for customizing media upload. It is not used by developers directly.
|
||||
type MediaOptions struct {
|
||||
ContentType string
|
||||
ForceEmptyContentType bool
|
||||
|
||||
ChunkSize int
|
||||
}
|
||||
|
||||
// ProcessMediaOptions stores options from opts in a MediaOptions.
|
||||
// It is not used by developers directly.
|
||||
type ResumableUpload struct {
|
||||
Client *http.Client
|
||||
// URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
|
||||
URI string
|
||||
UserAgent string // User-Agent for header of the request
|
||||
// Media is the object being uploaded.
|
||||
Media io.ReaderAt
|
||||
// MediaType defines the media type, e.g. "image/jpeg".
|
||||
MediaType string
|
||||
// ContentLength is the full size of the object being uploaded.
|
||||
ContentLength int64
|
||||
|
||||
mu sync.Mutex // guards progress
|
||||
progress int64 // number of bytes uploaded so far
|
||||
started bool // whether the upload has been started
|
||||
|
||||
// Callback is an optional function that will be called upon every progress update.
|
||||
Callback ProgressUpdater
|
||||
}
|
||||
|
||||
var (
|
||||
// rangeRE matches the transfer status response from the server. $1 is the last byte index uploaded.
|
||||
rangeRE = regexp.MustCompile(`^bytes=0\-(\d+)$`)
|
||||
// chunkSize is the size of the chunks created during a resumable upload and should be a power of two.
|
||||
// 1<<18 is the minimum size supported by the Google uploader, and there is no maximum.
|
||||
chunkSize int64 = 1 << 18
|
||||
)
|
||||
|
||||
// Progress returns the number of bytes uploaded at this point.
|
||||
func (rx *ResumableUpload) Progress() int64 {
|
||||
rx.mu.Lock()
|
||||
defer rx.mu.Unlock()
|
||||
return rx.progress
|
||||
}
|
||||
|
||||
func (rx *ResumableUpload) transferStatus() (int64, *http.Response, error) {
|
||||
req, _ := http.NewRequest("POST", rx.URI, nil)
|
||||
req.ContentLength = 0
|
||||
req.Header.Set("User-Agent", rx.UserAgent)
|
||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes */%v", rx.ContentLength))
|
||||
res, err := rx.Client.Do(req)
|
||||
if err != nil || res.StatusCode != statusResumeIncomplete {
|
||||
return 0, res, err
|
||||
func ProcessMediaOptions(opts []MediaOption) *MediaOptions {
|
||||
mo := &MediaOptions{ChunkSize: DefaultUploadChunkSize}
|
||||
for _, o := range opts {
|
||||
o.setOptions(mo)
|
||||
}
|
||||
var start int64
|
||||
if m := rangeRE.FindStringSubmatch(res.Header.Get("Range")); len(m) == 2 {
|
||||
start, err = strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("unable to parse range size %v", m[1])
|
||||
}
|
||||
start += 1 // Start at the next byte
|
||||
}
|
||||
return start, res, nil
|
||||
}
|
||||
|
||||
type chunk struct {
|
||||
body io.Reader
|
||||
size int64
|
||||
err error
|
||||
}
|
||||
|
||||
func (rx *ResumableUpload) transferChunks(ctx context.Context) (*http.Response, error) {
|
||||
var start int64
|
||||
var err error
|
||||
res := &http.Response{}
|
||||
if rx.started {
|
||||
start, res, err = rx.transferStatus()
|
||||
if err != nil || res.StatusCode != statusResumeIncomplete {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
rx.started = true
|
||||
|
||||
for {
|
||||
select { // Check for cancellation
|
||||
case <-ctx.Done():
|
||||
res.StatusCode = http.StatusRequestTimeout
|
||||
return res, ctx.Err()
|
||||
default:
|
||||
}
|
||||
reqSize := rx.ContentLength - start
|
||||
if reqSize > chunkSize {
|
||||
reqSize = chunkSize
|
||||
}
|
||||
r := io.NewSectionReader(rx.Media, start, reqSize)
|
||||
req, _ := http.NewRequest("POST", rx.URI, r)
|
||||
req.ContentLength = reqSize
|
||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes %v-%v/%v", start, start+reqSize-1, rx.ContentLength))
|
||||
req.Header.Set("Content-Type", rx.MediaType)
|
||||
req.Header.Set("User-Agent", rx.UserAgent)
|
||||
res, err = rx.Client.Do(req)
|
||||
start += reqSize
|
||||
if err == nil && (res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK) {
|
||||
rx.mu.Lock()
|
||||
rx.progress = start // keep track of number of bytes sent so far
|
||||
rx.mu.Unlock()
|
||||
if rx.Callback != nil {
|
||||
rx.Callback(start, rx.ContentLength)
|
||||
}
|
||||
}
|
||||
if err != nil || res.StatusCode != statusResumeIncomplete {
|
||||
break
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
var sleep = time.Sleep // override in unit tests
|
||||
|
||||
// Upload starts the process of a resumable upload with a cancellable context.
|
||||
// It retries indefinitely (with a pause of uploadPause between attempts) until cancelled.
|
||||
// It is called from the auto-generated API code and is not visible to the user.
|
||||
// rx is private to the auto-generated API code.
|
||||
func (rx *ResumableUpload) Upload(ctx context.Context) (*http.Response, error) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
for {
|
||||
res, err = rx.transferChunks(ctx)
|
||||
if err != nil || res.StatusCode == http.StatusCreated || res.StatusCode == http.StatusOK {
|
||||
return res, err
|
||||
}
|
||||
select { // Check for cancellation
|
||||
case <-ctx.Done():
|
||||
res.StatusCode = http.StatusRequestTimeout
|
||||
return res, ctx.Err()
|
||||
default:
|
||||
}
|
||||
sleep(uploadPause)
|
||||
}
|
||||
return res, err
|
||||
return mo
|
||||
}
|
||||
|
||||
func ResolveRelative(basestr, relstr string) string {
|
||||
|
@ -549,3 +392,33 @@ func CombineFields(s []Field) string {
|
|||
}
|
||||
return strings.Join(r, ",")
|
||||
}
|
||||
|
||||
// A CallOption is an optional argument to an API call.
|
||||
// It should be treated as an opaque value by users of Google APIs.
|
||||
//
|
||||
// A CallOption is something that configures an API call in a way that is
|
||||
// not specific to that API; for instance, controlling the quota user for
|
||||
// an API call is common across many APIs, and is thus a CallOption.
|
||||
type CallOption interface {
|
||||
Get() (key, value string)
|
||||
}
|
||||
|
||||
// QuotaUser returns a CallOption that will set the quota user for a call.
|
||||
// The quota user can be used by server-side applications to control accounting.
|
||||
// It can be an arbitrary string up to 40 characters, and will override UserIP
|
||||
// if both are provided.
|
||||
func QuotaUser(u string) CallOption { return quotaUser(u) }
|
||||
|
||||
type quotaUser string
|
||||
|
||||
func (q quotaUser) Get() (string, string) { return "quotaUser", string(q) }
|
||||
|
||||
// UserIP returns a CallOption that will set the "userIp" parameter of a call.
|
||||
// This should be the IP address of the originating request.
|
||||
func UserIP(ip string) CallOption { return userIP(ip) }
|
||||
|
||||
type userIP string
|
||||
|
||||
func (i userIP) Get() (string, string) { return "userIp", string(i) }
|
||||
|
||||
// TODO: Fields too
|
||||
|
|
247
vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
generated
vendored
247
vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
generated
vendored
|
@ -2,26 +2,15 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uritemplates is a level 4 implementation of RFC 6570 (URI
|
||||
// Package uritemplates is a level 3 implementation of RFC 6570 (URI
|
||||
// Template, http://tools.ietf.org/html/rfc6570).
|
||||
//
|
||||
// To use uritemplates, parse a template string and expand it with a value
|
||||
// map:
|
||||
//
|
||||
// template, _ := uritemplates.Parse("https://api.github.com/repos{/user,repo}")
|
||||
// values := make(map[string]interface{})
|
||||
// values["user"] = "jtacoma"
|
||||
// values["repo"] = "uritemplates"
|
||||
// expanded, _ := template.ExpandString(values)
|
||||
// fmt.Printf(expanded)
|
||||
//
|
||||
// uritemplates does not support composite values (in Go: slices or maps)
|
||||
// and so does not qualify as a level 4 implementation.
|
||||
package uritemplates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -45,52 +34,47 @@ func pctEncode(src []byte) []byte {
|
|||
return dst
|
||||
}
|
||||
|
||||
func escape(s string, allowReserved bool) (escaped string) {
|
||||
func escape(s string, allowReserved bool) string {
|
||||
if allowReserved {
|
||||
escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
} else {
|
||||
escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
return string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
return escaped
|
||||
return string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
|
||||
// A UriTemplate is a parsed representation of a URI template.
|
||||
type UriTemplate struct {
|
||||
// A uriTemplate is a parsed representation of a URI template.
|
||||
type uriTemplate struct {
|
||||
raw string
|
||||
parts []templatePart
|
||||
}
|
||||
|
||||
// Parse parses a URI template string into a UriTemplate object.
|
||||
func Parse(rawtemplate string) (template *UriTemplate, err error) {
|
||||
template = new(UriTemplate)
|
||||
template.raw = rawtemplate
|
||||
split := strings.Split(rawtemplate, "{")
|
||||
template.parts = make([]templatePart, len(split)*2-1)
|
||||
// parse parses a URI template string into a uriTemplate object.
|
||||
func parse(rawTemplate string) (*uriTemplate, error) {
|
||||
split := strings.Split(rawTemplate, "{")
|
||||
parts := make([]templatePart, len(split)*2-1)
|
||||
for i, s := range split {
|
||||
if i == 0 {
|
||||
if strings.Contains(s, "}") {
|
||||
err = errors.New("unexpected }")
|
||||
break
|
||||
return nil, errors.New("unexpected }")
|
||||
}
|
||||
template.parts[i].raw = s
|
||||
} else {
|
||||
subsplit := strings.Split(s, "}")
|
||||
if len(subsplit) != 2 {
|
||||
err = errors.New("malformed template")
|
||||
break
|
||||
}
|
||||
expression := subsplit[0]
|
||||
template.parts[i*2-1], err = parseExpression(expression)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
template.parts[i*2].raw = subsplit[1]
|
||||
parts[i].raw = s
|
||||
continue
|
||||
}
|
||||
subsplit := strings.Split(s, "}")
|
||||
if len(subsplit) != 2 {
|
||||
return nil, errors.New("malformed template")
|
||||
}
|
||||
expression := subsplit[0]
|
||||
var err error
|
||||
parts[i*2-1], err = parseExpression(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts[i*2].raw = subsplit[1]
|
||||
}
|
||||
if err != nil {
|
||||
template = nil
|
||||
}
|
||||
return template, err
|
||||
return &uriTemplate{
|
||||
raw: rawTemplate,
|
||||
parts: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type templatePart struct {
|
||||
|
@ -160,6 +144,8 @@ func parseExpression(expression string) (result templatePart, err error) {
|
|||
}
|
||||
|
||||
func parseTerm(term string) (result templateTerm, err error) {
|
||||
// TODO(djd): Remove "*" suffix parsing once we check that no APIs have
|
||||
// mistakenly used that attribute.
|
||||
if strings.HasSuffix(term, "*") {
|
||||
result.explode = true
|
||||
term = term[:len(term)-1]
|
||||
|
@ -185,175 +171,50 @@ func parseTerm(term string) (result templateTerm, err error) {
|
|||
}
|
||||
|
||||
// Expand expands a URI template with a set of values to produce a string.
|
||||
func (self *UriTemplate) Expand(value interface{}) (string, error) {
|
||||
values, ismap := value.(map[string]interface{})
|
||||
if !ismap {
|
||||
if m, ismap := struct2map(value); !ismap {
|
||||
return "", errors.New("expected map[string]interface{}, struct, or pointer to struct.")
|
||||
} else {
|
||||
return self.Expand(m)
|
||||
}
|
||||
}
|
||||
func (t *uriTemplate) Expand(values map[string]string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, p := range self.parts {
|
||||
err := p.expand(&buf, values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, p := range t.parts {
|
||||
p.expand(&buf, values)
|
||||
}
|
||||
return buf.String(), nil
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (self *templatePart) expand(buf *bytes.Buffer, values map[string]interface{}) error {
|
||||
if len(self.raw) > 0 {
|
||||
buf.WriteString(self.raw)
|
||||
return nil
|
||||
func (tp *templatePart) expand(buf *bytes.Buffer, values map[string]string) {
|
||||
if len(tp.raw) > 0 {
|
||||
buf.WriteString(tp.raw)
|
||||
return
|
||||
}
|
||||
var zeroLen = buf.Len()
|
||||
buf.WriteString(self.first)
|
||||
var firstLen = buf.Len()
|
||||
for _, term := range self.terms {
|
||||
var first = true
|
||||
for _, term := range tp.terms {
|
||||
value, exists := values[term.name]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if buf.Len() != firstLen {
|
||||
buf.WriteString(self.sep)
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
self.expandString(buf, term, v)
|
||||
case []interface{}:
|
||||
self.expandArray(buf, term, v)
|
||||
case map[string]interface{}:
|
||||
if term.truncate > 0 {
|
||||
return errors.New("cannot truncate a map expansion")
|
||||
}
|
||||
self.expandMap(buf, term, v)
|
||||
default:
|
||||
if m, ismap := struct2map(value); ismap {
|
||||
if term.truncate > 0 {
|
||||
return errors.New("cannot truncate a map expansion")
|
||||
}
|
||||
self.expandMap(buf, term, m)
|
||||
} else {
|
||||
str := fmt.Sprintf("%v", value)
|
||||
self.expandString(buf, term, str)
|
||||
}
|
||||
if first {
|
||||
buf.WriteString(tp.first)
|
||||
first = false
|
||||
} else {
|
||||
buf.WriteString(tp.sep)
|
||||
}
|
||||
tp.expandString(buf, term, value)
|
||||
}
|
||||
if buf.Len() == firstLen {
|
||||
original := buf.Bytes()[:zeroLen]
|
||||
buf.Reset()
|
||||
buf.Write(original)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
|
||||
if self.named {
|
||||
func (tp *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
|
||||
if tp.named {
|
||||
buf.WriteString(name)
|
||||
if empty {
|
||||
buf.WriteString(self.ifemp)
|
||||
buf.WriteString(tp.ifemp)
|
||||
} else {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
|
||||
func (tp *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
self.expandName(buf, t.name, len(s) == 0)
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
|
||||
func (self *templatePart) expandArray(buf *bytes.Buffer, t templateTerm, a []interface{}) {
|
||||
if len(a) == 0 {
|
||||
return
|
||||
} else if !t.explode {
|
||||
self.expandName(buf, t.name, false)
|
||||
}
|
||||
for i, value := range a {
|
||||
if t.explode && i > 0 {
|
||||
buf.WriteString(self.sep)
|
||||
} else if i > 0 {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
if self.named && t.explode {
|
||||
self.expandName(buf, t.name, len(s) == 0)
|
||||
}
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *templatePart) expandMap(buf *bytes.Buffer, t templateTerm, m map[string]interface{}) {
|
||||
if len(m) == 0 {
|
||||
return
|
||||
}
|
||||
if !t.explode {
|
||||
self.expandName(buf, t.name, len(m) == 0)
|
||||
}
|
||||
var firstLen = buf.Len()
|
||||
for k, value := range m {
|
||||
if firstLen != buf.Len() {
|
||||
if t.explode {
|
||||
buf.WriteString(self.sep)
|
||||
} else {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if t.explode {
|
||||
buf.WriteString(escape(k, self.allowReserved))
|
||||
buf.WriteRune('=')
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
} else {
|
||||
buf.WriteString(escape(k, self.allowReserved))
|
||||
buf.WriteRune(',')
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func struct2map(v interface{}) (map[string]interface{}, bool) {
|
||||
value := reflect.ValueOf(v)
|
||||
switch value.Type().Kind() {
|
||||
case reflect.Ptr:
|
||||
return struct2map(value.Elem().Interface())
|
||||
case reflect.Struct:
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
tag := value.Type().Field(i).Tag
|
||||
var name string
|
||||
if strings.Contains(string(tag), ":") {
|
||||
name = tag.Get("uri")
|
||||
} else {
|
||||
name = strings.TrimSpace(string(tag))
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = value.Type().Field(i).Name
|
||||
}
|
||||
m[name] = value.Field(i).Interface()
|
||||
}
|
||||
return m, true
|
||||
}
|
||||
return nil, false
|
||||
tp.expandName(buf, t.name, len(s) == 0)
|
||||
buf.WriteString(escape(s, tp.allowReserved))
|
||||
}
|
||||
|
|
14
vendor/google.golang.org/api/googleapi/internal/uritemplates/utils.go
generated
vendored
14
vendor/google.golang.org/api/googleapi/internal/uritemplates/utils.go
generated
vendored
|
@ -1,13 +1,13 @@
|
|||
// 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 uritemplates
|
||||
|
||||
func Expand(path string, expansions map[string]string) (string, error) {
|
||||
template, err := Parse(path)
|
||||
func Expand(path string, values map[string]string) (string, error) {
|
||||
template, err := parse(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
values := make(map[string]interface{})
|
||||
for k, v := range expansions {
|
||||
values[k] = v
|
||||
}
|
||||
return template.Expand(values)
|
||||
return template.Expand(values), nil
|
||||
}
|
||||
|
|
32
vendor/google.golang.org/api/googleapi/types.go
generated
vendored
32
vendor/google.golang.org/api/googleapi/types.go
generated
vendored
|
@ -148,3 +148,35 @@ func (s Float64s) MarshalJSON() ([]byte, error) {
|
|||
return strconv.AppendFloat(dst, s[i], 'g', -1, 64)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||
*/
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value
|
||||
// to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool { return &v }
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int32(v int32) *int32 { return &v }
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int64(v int64) *int64 { return &v }
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float64(v float64) *float64 { return &v }
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint32(v uint32) *uint32 { return &v }
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint64(v uint64) *uint64 { return &v }
|
||||
|
||||
// String is a helper routine that allocates a new string value
|
||||
// to store v and returns a pointer to it.
|
||||
func String(v string) *string { return &v }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue