mirror of https://github.com/grafana/grafana
parent
7e3c9fcc1c
commit
d0ab028cfc
@ -1,27 +0,0 @@ |
||||
Want to contribute? Great! First, read this page (including the small print at the end). |
||||
|
||||
### Before you contribute |
||||
Before we can use your code, you must sign the |
||||
[Google Individual Contributor License Agreement] |
||||
(https://cla.developers.google.com/about/google-individual) |
||||
(CLA), which you can do online. The CLA is necessary mainly because you own the |
||||
copyright to your changes, even after your contribution becomes part of our |
||||
codebase, so we need your permission to use and distribute your code. We also |
||||
need to be sure of various other things—for instance that you'll tell us if you |
||||
know that your code infringes on other people's patents. You don't have to sign |
||||
the CLA until after you've submitted your code for review and a member has |
||||
approved it, but you must do it before we can put your code into our codebase. |
||||
Before you start working on a larger contribution, you should get in touch with |
||||
us first through the issue tracker with your idea so that we can help out and |
||||
possibly guide you. Coordinating up front makes it much easier to avoid |
||||
frustration later on. |
||||
|
||||
### Code reviews |
||||
All submissions, including submissions by project members, require review. We |
||||
use Github pull requests for this purpose. |
||||
|
||||
### The small print |
||||
Contributions made by corporations are covered by a different agreement than |
||||
the one above, the |
||||
[Software Grant and Corporate Contributor License Agreement] |
||||
(https://cla.developers.google.com/about/google-corporate). |
@ -1,27 +0,0 @@ |
||||
Copyright 2016, Google Inc. |
||||
All rights reserved. |
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are |
||||
met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above |
||||
copyright notice, this list of conditions and the following disclaimer |
||||
in the documentation and/or other materials provided with the |
||||
distribution. |
||||
* Neither the name of Google Inc. nor the names of its |
||||
contributors may be used to endorse or promote products derived from |
||||
this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -1,24 +0,0 @@ |
||||
Google API Extensions for Go |
||||
============================ |
||||
|
||||
[](https://travis-ci.org/googleapis/gax-go) |
||||
[](https://codecov.io/github/googleapis/gax-go) |
||||
|
||||
Google API Extensions for Go (gax-go) is a set of modules which aids the |
||||
development of APIs for clients and servers based on `gRPC` and Google API |
||||
conventions. |
||||
|
||||
Application code will rarely need to use this library directly, |
||||
but the code generated automatically from API definition files can use it |
||||
to simplify code generation and to provide more convenient and idiomatic API surface. |
||||
|
||||
**This project is currently experimental and not supported.** |
||||
|
||||
Go Versions |
||||
=========== |
||||
This library requires Go 1.6 or above. |
||||
|
||||
License |
||||
======= |
||||
BSD - please see [LICENSE](https://github.com/googleapis/gax-go/blob/master/LICENSE) |
||||
for more information. |
@ -1,157 +0,0 @@ |
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax |
||||
|
||||
import ( |
||||
"math/rand" |
||||
"time" |
||||
|
||||
"google.golang.org/grpc" |
||||
"google.golang.org/grpc/codes" |
||||
"google.golang.org/grpc/status" |
||||
) |
||||
|
||||
// CallOption is an option used by Invoke to control behaviors of RPC calls.
|
||||
// CallOption works by modifying relevant fields of CallSettings.
|
||||
type CallOption interface { |
||||
// Resolve applies the option by modifying cs.
|
||||
Resolve(cs *CallSettings) |
||||
} |
||||
|
||||
// Retryer is used by Invoke to determine retry behavior.
|
||||
type Retryer interface { |
||||
// Retry reports whether a request should be retriedand how long to pause before retrying
|
||||
// if the previous attempt returned with err. Invoke never calls Retry with nil error.
|
||||
Retry(err error) (pause time.Duration, shouldRetry bool) |
||||
} |
||||
|
||||
type retryerOption func() Retryer |
||||
|
||||
func (o retryerOption) Resolve(s *CallSettings) { |
||||
s.Retry = o |
||||
} |
||||
|
||||
// WithRetry sets CallSettings.Retry to fn.
|
||||
func WithRetry(fn func() Retryer) CallOption { |
||||
return retryerOption(fn) |
||||
} |
||||
|
||||
// OnCodes returns a Retryer that retries if and only if
|
||||
// the previous attempt returns a GRPC error whose error code is stored in cc.
|
||||
// Pause times between retries are specified by bo.
|
||||
//
|
||||
// bo is only used for its parameters; each Retryer has its own copy.
|
||||
func OnCodes(cc []codes.Code, bo Backoff) Retryer { |
||||
return &boRetryer{ |
||||
backoff: bo, |
||||
codes: append([]codes.Code(nil), cc...), |
||||
} |
||||
} |
||||
|
||||
type boRetryer struct { |
||||
backoff Backoff |
||||
codes []codes.Code |
||||
} |
||||
|
||||
func (r *boRetryer) Retry(err error) (time.Duration, bool) { |
||||
st, ok := status.FromError(err) |
||||
if !ok { |
||||
return 0, false |
||||
} |
||||
c := st.Code() |
||||
for _, rc := range r.codes { |
||||
if c == rc { |
||||
return r.backoff.Pause(), true |
||||
} |
||||
} |
||||
return 0, false |
||||
} |
||||
|
||||
// Backoff implements exponential backoff.
|
||||
// The wait time between retries is a random value between 0 and the "retry envelope".
|
||||
// The envelope starts at Initial and increases by the factor of Multiplier every retry,
|
||||
// but is capped at Max.
|
||||
type Backoff struct { |
||||
// Initial is the initial value of the retry envelope, defaults to 1 second.
|
||||
Initial time.Duration |
||||
|
||||
// Max is the maximum value of the retry envelope, defaults to 30 seconds.
|
||||
Max time.Duration |
||||
|
||||
// Multiplier is the factor by which the retry envelope increases.
|
||||
// It should be greater than 1 and defaults to 2.
|
||||
Multiplier float64 |
||||
|
||||
// cur is the current retry envelope
|
||||
cur time.Duration |
||||
} |
||||
|
||||
func (bo *Backoff) Pause() time.Duration { |
||||
if bo.Initial == 0 { |
||||
bo.Initial = time.Second |
||||
} |
||||
if bo.cur == 0 { |
||||
bo.cur = bo.Initial |
||||
} |
||||
if bo.Max == 0 { |
||||
bo.Max = 30 * time.Second |
||||
} |
||||
if bo.Multiplier < 1 { |
||||
bo.Multiplier = 2 |
||||
} |
||||
// Select a duration between zero and the current max. It might seem counterintuitive to
|
||||
// have so much jitter, but https://www.awsarchitectureblog.com/2015/03/backoff.html
|
||||
// argues that that is the best strategy.
|
||||
d := time.Duration(rand.Int63n(int64(bo.cur))) |
||||
bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier) |
||||
if bo.cur > bo.Max { |
||||
bo.cur = bo.Max |
||||
} |
||||
return d |
||||
} |
||||
|
||||
type grpcOpt []grpc.CallOption |
||||
|
||||
func (o grpcOpt) Resolve(s *CallSettings) { |
||||
s.GRPC = o |
||||
} |
||||
|
||||
func WithGRPCOptions(opt ...grpc.CallOption) CallOption { |
||||
return grpcOpt(append([]grpc.CallOption(nil), opt...)) |
||||
} |
||||
|
||||
type CallSettings struct { |
||||
// Retry returns a Retryer to be used to control retry logic of a method call.
|
||||
// If Retry is nil or the returned Retryer is nil, the call will not be retried.
|
||||
Retry func() Retryer |
||||
|
||||
// CallOptions to be forwarded to GRPC.
|
||||
GRPC []grpc.CallOption |
||||
} |
@ -1,40 +0,0 @@ |
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package gax contains a set of modules which aid the development of APIs
|
||||
// for clients and servers based on gRPC and Google API conventions.
|
||||
//
|
||||
// Application code will rarely need to use this library directly.
|
||||
// However, code generated automatically from API definition files can use it
|
||||
// to simplify code generation and to provide more convenient and idiomatic API surfaces.
|
||||
//
|
||||
// This project is currently experimental and not supported.
|
||||
package gax |
||||
|
||||
const Version = "0.1.0" |
@ -1,24 +0,0 @@ |
||||
package gax |
||||
|
||||
import "bytes" |
||||
|
||||
// XGoogHeader is for use by the Google Cloud Libraries only.
|
||||
//
|
||||
// XGoogHeader formats key-value pairs.
|
||||
// The resulting string is suitable for x-goog-api-client header.
|
||||
func XGoogHeader(keyval ...string) string { |
||||
if len(keyval) == 0 { |
||||
return "" |
||||
} |
||||
if len(keyval)%2 != 0 { |
||||
panic("gax.Header: odd argument count") |
||||
} |
||||
var buf bytes.Buffer |
||||
for i := 0; i < len(keyval); i += 2 { |
||||
buf.WriteByte(' ') |
||||
buf.WriteString(keyval[i]) |
||||
buf.WriteByte('/') |
||||
buf.WriteString(keyval[i+1]) |
||||
} |
||||
return buf.String()[1:] |
||||
} |
@ -1,90 +0,0 @@ |
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"golang.org/x/net/context" |
||||
) |
||||
|
||||
// A user defined call stub.
|
||||
type APICall func(context.Context, CallSettings) error |
||||
|
||||
// Invoke calls the given APICall,
|
||||
// performing retries as specified by opts, if any.
|
||||
func Invoke(ctx context.Context, call APICall, opts ...CallOption) error { |
||||
var settings CallSettings |
||||
for _, opt := range opts { |
||||
opt.Resolve(&settings) |
||||
} |
||||
return invoke(ctx, call, settings, Sleep) |
||||
} |
||||
|
||||
// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
|
||||
// If interrupted, Sleep returns ctx.Err().
|
||||
func Sleep(ctx context.Context, d time.Duration) error { |
||||
t := time.NewTimer(d) |
||||
select { |
||||
case <-ctx.Done(): |
||||
t.Stop() |
||||
return ctx.Err() |
||||
case <-t.C: |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
type sleeper func(ctx context.Context, d time.Duration) error |
||||
|
||||
// invoke implements Invoke, taking an additional sleeper argument for testing.
|
||||
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error { |
||||
var retryer Retryer |
||||
for { |
||||
err := call(ctx, settings) |
||||
if err == nil { |
||||
return nil |
||||
} |
||||
if settings.Retry == nil { |
||||
return err |
||||
} |
||||
if retryer == nil { |
||||
if r := settings.Retry(); r != nil { |
||||
retryer = r |
||||
} else { |
||||
return err |
||||
} |
||||
} |
||||
if d, ok := retryer.Retry(err); !ok { |
||||
return err |
||||
} else if err = sp(ctx, d); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
@ -1,176 +0,0 @@ |
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
type matcher interface { |
||||
match([]string) (int, error) |
||||
String() string |
||||
} |
||||
|
||||
type segment struct { |
||||
matcher |
||||
name string |
||||
} |
||||
|
||||
type labelMatcher string |
||||
|
||||
func (ls labelMatcher) match(segments []string) (int, error) { |
||||
if len(segments) == 0 { |
||||
return 0, fmt.Errorf("expected %s but no more segments found", ls) |
||||
} |
||||
if segments[0] != string(ls) { |
||||
return 0, fmt.Errorf("expected %s but got %s", ls, segments[0]) |
||||
} |
||||
return 1, nil |
||||
} |
||||
|
||||
func (ls labelMatcher) String() string { |
||||
return string(ls) |
||||
} |
||||
|
||||
type wildcardMatcher int |
||||
|
||||
func (wm wildcardMatcher) match(segments []string) (int, error) { |
||||
if len(segments) == 0 { |
||||
return 0, errors.New("no more segments found") |
||||
} |
||||
return 1, nil |
||||
} |
||||
|
||||
func (wm wildcardMatcher) String() string { |
||||
return "*" |
||||
} |
||||
|
||||
type pathWildcardMatcher int |
||||
|
||||
func (pwm pathWildcardMatcher) match(segments []string) (int, error) { |
||||
length := len(segments) - int(pwm) |
||||
if length <= 0 { |
||||
return 0, errors.New("not sufficient segments are supplied for path wildcard") |
||||
} |
||||
return length, nil |
||||
} |
||||
|
||||
func (pwm pathWildcardMatcher) String() string { |
||||
return "**" |
||||
} |
||||
|
||||
type ParseError struct { |
||||
Pos int |
||||
Template string |
||||
Message string |
||||
} |
||||
|
||||
func (pe ParseError) Error() string { |
||||
return fmt.Sprintf("at %d of template '%s', %s", pe.Pos, pe.Template, pe.Message) |
||||
} |
||||
|
||||
// PathTemplate manages the template to build and match with paths used
|
||||
// by API services. It holds a template and variable names in it, and
|
||||
// it can extract matched patterns from a path string or build a path
|
||||
// string from a binding.
|
||||
//
|
||||
// See http.proto in github.com/googleapis/googleapis/ for the details of
|
||||
// the template syntax.
|
||||
type PathTemplate struct { |
||||
segments []segment |
||||
} |
||||
|
||||
// NewPathTemplate parses a path template, and returns a PathTemplate
|
||||
// instance if successful.
|
||||
func NewPathTemplate(template string) (*PathTemplate, error) { |
||||
return parsePathTemplate(template) |
||||
} |
||||
|
||||
// MustCompilePathTemplate is like NewPathTemplate but panics if the
|
||||
// expression cannot be parsed. It simplifies safe initialization of
|
||||
// global variables holding compiled regular expressions.
|
||||
func MustCompilePathTemplate(template string) *PathTemplate { |
||||
pt, err := NewPathTemplate(template) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
return pt |
||||
} |
||||
|
||||
// Match attempts to match the given path with the template, and returns
|
||||
// the mapping of the variable name to the matched pattern string.
|
||||
func (pt *PathTemplate) Match(path string) (map[string]string, error) { |
||||
paths := strings.Split(path, "/") |
||||
values := map[string]string{} |
||||
for _, segment := range pt.segments { |
||||
length, err := segment.match(paths) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if segment.name != "" { |
||||
value := strings.Join(paths[:length], "/") |
||||
if oldValue, ok := values[segment.name]; ok { |
||||
values[segment.name] = oldValue + "/" + value |
||||
} else { |
||||
values[segment.name] = value |
||||
} |
||||
} |
||||
paths = paths[length:] |
||||
} |
||||
if len(paths) != 0 { |
||||
return nil, fmt.Errorf("Trailing path %s remains after the matching", strings.Join(paths, "/")) |
||||
} |
||||
return values, nil |
||||
} |
||||
|
||||
// Render creates a path string from its template and the binding from
|
||||
// the variable name to the value.
|
||||
func (pt *PathTemplate) Render(binding map[string]string) (string, error) { |
||||
result := make([]string, 0, len(pt.segments)) |
||||
var lastVariableName string |
||||
for _, segment := range pt.segments { |
||||
name := segment.name |
||||
if lastVariableName != "" && name == lastVariableName { |
||||
continue |
||||
} |
||||
lastVariableName = name |
||||
if name == "" { |
||||
result = append(result, segment.String()) |
||||
} else if value, ok := binding[name]; ok { |
||||
result = append(result, value) |
||||
} else { |
||||
return "", fmt.Errorf("%s is not found", name) |
||||
} |
||||
} |
||||
built := strings.Join(result, "/") |
||||
return built, nil |
||||
} |
@ -1,227 +0,0 @@ |
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io" |
||||
"strings" |
||||
) |
||||
|
||||
// This parser follows the syntax of path templates, from
|
||||
// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto.
|
||||
// The differences are that there is no custom verb, we allow the initial slash
|
||||
// to be absent, and that we are not strict as
|
||||
// https://tools.ietf.org/html/rfc6570 about the characters in identifiers and
|
||||
// literals.
|
||||
|
||||
type pathTemplateParser struct { |
||||
r *strings.Reader |
||||
runeCount int // the number of the current rune in the original string
|
||||
nextVar int // the number to use for the next unnamed variable
|
||||
seenName map[string]bool // names we've seen already
|
||||
seenPathWildcard bool // have we seen "**" already?
|
||||
} |
||||
|
||||
func parsePathTemplate(template string) (pt *PathTemplate, err error) { |
||||
p := &pathTemplateParser{ |
||||
r: strings.NewReader(template), |
||||
seenName: map[string]bool{}, |
||||
} |
||||
|
||||
// Handle panics with strings like errors.
|
||||
// See pathTemplateParser.error, below.
|
||||
defer func() { |
||||
if x := recover(); x != nil { |
||||
errmsg, ok := x.(errString) |
||||
if !ok { |
||||
panic(x) |
||||
} |
||||
pt = nil |
||||
err = ParseError{p.runeCount, template, string(errmsg)} |
||||
} |
||||
}() |
||||
|
||||
segs := p.template() |
||||
// If there is a path wildcard, set its length. We can't do this
|
||||
// until we know how many segments we've got all together.
|
||||
for i, seg := range segs { |
||||
if _, ok := seg.matcher.(pathWildcardMatcher); ok { |
||||
segs[i].matcher = pathWildcardMatcher(len(segs) - i - 1) |
||||
break |
||||
} |
||||
} |
||||
return &PathTemplate{segments: segs}, nil |
||||
|
||||
} |
||||
|
||||
// Used to indicate errors "thrown" by this parser. We don't use string because
|
||||
// many parts of the standard library panic with strings.
|
||||
type errString string |
||||
|
||||
// Terminates parsing immediately with an error.
|
||||
func (p *pathTemplateParser) error(msg string) { |
||||
panic(errString(msg)) |
||||
} |
||||
|
||||
// Template = [ "/" ] Segments
|
||||
func (p *pathTemplateParser) template() []segment { |
||||
var segs []segment |
||||
if p.consume('/') { |
||||
// Initial '/' needs an initial empty matcher.
|
||||
segs = append(segs, segment{matcher: labelMatcher("")}) |
||||
} |
||||
return append(segs, p.segments("")...) |
||||
} |
||||
|
||||
// Segments = Segment { "/" Segment }
|
||||
func (p *pathTemplateParser) segments(name string) []segment { |
||||
var segs []segment |
||||
for { |
||||
subsegs := p.segment(name) |
||||
segs = append(segs, subsegs...) |
||||
if !p.consume('/') { |
||||
break |
||||
} |
||||
} |
||||
return segs |
||||
} |
||||
|
||||
// Segment = "*" | "**" | LITERAL | Variable
|
||||
func (p *pathTemplateParser) segment(name string) []segment { |
||||
if p.consume('*') { |
||||
if name == "" { |
||||
name = fmt.Sprintf("$%d", p.nextVar) |
||||
p.nextVar++ |
||||
} |
||||
if p.consume('*') { |
||||
if p.seenPathWildcard { |
||||
p.error("multiple '**' disallowed") |
||||
} |
||||
p.seenPathWildcard = true |
||||
// We'll change 0 to the right number at the end.
|
||||
return []segment{{name: name, matcher: pathWildcardMatcher(0)}} |
||||
} |
||||
return []segment{{name: name, matcher: wildcardMatcher(0)}} |
||||
} |
||||
if p.consume('{') { |
||||
if name != "" { |
||||
p.error("recursive named bindings are not allowed") |
||||
} |
||||
return p.variable() |
||||
} |
||||
return []segment{{name: name, matcher: labelMatcher(p.literal())}} |
||||
} |
||||
|
||||
// Variable = "{" FieldPath [ "=" Segments ] "}"
|
||||
// "{" is already consumed.
|
||||
func (p *pathTemplateParser) variable() []segment { |
||||
// Simplification: treat FieldPath as LITERAL, instead of IDENT { '.' IDENT }
|
||||
name := p.literal() |
||||
if p.seenName[name] { |
||||
p.error(name + " appears multiple times") |
||||
} |
||||
p.seenName[name] = true |
||||
var segs []segment |
||||
if p.consume('=') { |
||||
segs = p.segments(name) |
||||
} else { |
||||
// "{var}" is equivalent to "{var=*}"
|
||||
segs = []segment{{name: name, matcher: wildcardMatcher(0)}} |
||||
} |
||||
if !p.consume('}') { |
||||
p.error("expected '}'") |
||||
} |
||||
return segs |
||||
} |
||||
|
||||
// A literal is any sequence of characters other than a few special ones.
|
||||
// The list of stop characters is not quite the same as in the template RFC.
|
||||
func (p *pathTemplateParser) literal() string { |
||||
lit := p.consumeUntil("/*}{=") |
||||
if lit == "" { |
||||
p.error("empty literal") |
||||
} |
||||
return lit |
||||
} |
||||
|
||||
// Read runes until EOF or one of the runes in stopRunes is encountered.
|
||||
// If the latter, unread the stop rune. Return the accumulated runes as a string.
|
||||
func (p *pathTemplateParser) consumeUntil(stopRunes string) string { |
||||
var runes []rune |
||||
for { |
||||
r, ok := p.readRune() |
||||
if !ok { |
||||
break |
||||
} |
||||
if strings.IndexRune(stopRunes, r) >= 0 { |
||||
p.unreadRune() |
||||
break |
||||
} |
||||
runes = append(runes, r) |
||||
} |
||||
return string(runes) |
||||
} |
||||
|
||||
// If the next rune is r, consume it and return true.
|
||||
// Otherwise, leave the input unchanged and return false.
|
||||
func (p *pathTemplateParser) consume(r rune) bool { |
||||
rr, ok := p.readRune() |
||||
if !ok { |
||||
return false |
||||
} |
||||
if r == rr { |
||||
return true |
||||
} |
||||
p.unreadRune() |
||||
return false |
||||
} |
||||
|
||||
// Read the next rune from the input. Return it.
|
||||
// The second return value is false at EOF.
|
||||
func (p *pathTemplateParser) readRune() (rune, bool) { |
||||
r, _, err := p.r.ReadRune() |
||||
if err == io.EOF { |
||||
return r, false |
||||
} |
||||
if err != nil { |
||||
p.error(err.Error()) |
||||
} |
||||
p.runeCount++ |
||||
return r, true |
||||
} |
||||
|
||||
// Put the last rune that was read back on the input.
|
||||
func (p *pathTemplateParser) unreadRune() { |
||||
if err := p.r.UnreadRune(); err != nil { |
||||
p.error(err.Error()) |
||||
} |
||||
p.runeCount-- |
||||
} |
Loading…
Reference in new issue