diff options
Diffstat (limited to 'vendor')
34 files changed, 1947 insertions, 206 deletions
diff --git a/vendor/github.com/99designs/gqlgen/api/generate.go b/vendor/github.com/99designs/gqlgen/api/generate.go index 3dd083f5..3256bdc3 100644 --- a/vendor/github.com/99designs/gqlgen/api/generate.go +++ b/vendor/github.com/99designs/gqlgen/api/generate.go @@ -8,6 +8,7 @@ import ( "github.com/99designs/gqlgen/plugin" "github.com/99designs/gqlgen/plugin/modelgen" "github.com/99designs/gqlgen/plugin/resolvergen" + "github.com/99designs/gqlgen/plugin/schemaconfig" "github.com/pkg/errors" "golang.org/x/tools/go/packages" ) @@ -17,6 +18,7 @@ func Generate(cfg *config.Config, option ...Option) error { _ = syscall.Unlink(cfg.Model.Filename) plugins := []plugin.Plugin{ + schemaconfig.New(), modelgen.New(), resolvergen.New(), } diff --git a/vendor/github.com/99designs/gqlgen/codegen/args.go b/vendor/github.com/99designs/gqlgen/codegen/args.go index d1498bdd..1d3e51aa 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/args.go +++ b/vendor/github.com/99designs/gqlgen/codegen/args.go @@ -26,6 +26,22 @@ type FieldArgument struct { Value interface{} // value set in Data } +//ImplDirectives get not Builtin and location ARGUMENT_DEFINITION directive +func (f *FieldArgument) ImplDirectives() []*Directive { + d := make([]*Directive, 0) + for i := range f.Directives { + if !f.Directives[i].Builtin && f.Directives[i].IsLocation(ast.LocationArgumentDefinition) { + d = append(d, f.Directives[i]) + } + } + + return d +} + +func (f *FieldArgument) DirectiveObjName() string { + return "rawArgs" +} + func (f *FieldArgument) Stream() bool { return f.Object != nil && f.Object.Stream } diff --git a/vendor/github.com/99designs/gqlgen/codegen/args.gotpl b/vendor/github.com/99designs/gqlgen/codegen/args.gotpl index 4c721218..c76bf0f7 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/args.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/args.gotpl @@ -5,22 +5,10 @@ func (ec *executionContext) {{ $name }}(ctx context.Context, rawArgs map[string] {{- range $i, $arg := . }} var arg{{$i}} {{ $arg.TypeReference.GO | ref}} if tmp, ok := rawArgs[{{$arg.Name|quote}}]; ok { - {{- if $arg.Directives }} - getArg0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $arg.TypeReference.UnmarshalFunc }}(ctx, tmp) } - - {{- range $i, $directive := $arg.Directives }} - getArg{{add $i 1}} := func(ctx context.Context) (res interface{}, err error) { - {{- range $dArg := $directive.Args }} - {{- if and $dArg.TypeReference.IsPtr ( notNil "Value" $dArg ) }} - {{ $dArg.VarName }} := {{ $dArg.Value | dump }} - {{- end }} - {{- end }} - n := getArg{{$i}} - return ec.directives.{{$directive.Name|ucFirst}}({{$directive.ResolveArgs "tmp" "n" }}) - } - {{- end }} - - tmp, err = getArg{{$arg.Directives|len}}(ctx) + {{- if $arg.ImplDirectives }} + directive0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $arg.TypeReference.UnmarshalFunc }}(ctx, tmp) } + {{ template "implDirectives" $arg }} + tmp, err = directive{{$arg.ImplDirectives|len}}(ctx) if err != nil { return nil, err } diff --git a/vendor/github.com/99designs/gqlgen/codegen/config/binder.go b/vendor/github.com/99designs/gqlgen/codegen/config/binder.go index cea904ad..72956de4 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/config/binder.go +++ b/vendor/github.com/99designs/gqlgen/codegen/config/binder.go @@ -14,7 +14,7 @@ import ( // Binder connects graphql types to golang types using static analysis type Binder struct { - pkgs []*packages.Package + pkgs map[string]*packages.Package schema *ast.Schema cfg *Config References []*TypeReference @@ -26,7 +26,9 @@ func (c *Config) NewBinder(s *ast.Schema) (*Binder, error) { return nil, err } + mp := map[string]*packages.Package{} for _, p := range pkgs { + populatePkg(mp, p) for _, e := range p.Errors { if e.Kind == packages.ListError { return nil, p.Errors[0] @@ -35,12 +37,23 @@ func (c *Config) NewBinder(s *ast.Schema) (*Binder, error) { } return &Binder{ - pkgs: pkgs, + pkgs: mp, schema: s, cfg: c, }, nil } +func populatePkg(mp map[string]*packages.Package, p *packages.Package) { + imp := code.NormalizeVendor(p.PkgPath) + if _, ok := mp[imp]; ok { + return + } + mp[imp] = p + for _, p := range p.Imports { + populatePkg(mp, p) + } +} + func (b *Binder) TypePosition(typ types.Type) token.Position { named, isNamed := typ.(*types.Named) if !isNamed { @@ -75,10 +88,9 @@ func (b *Binder) FindType(pkgName string, typeName string) (types.Type, error) { } func (b *Binder) getPkg(find string) *packages.Package { - for _, p := range b.pkgs { - if code.NormalizeVendor(find) == code.NormalizeVendor(p.PkgPath) { - return p - } + imp := code.NormalizeVendor(find) + if p, ok := b.pkgs[imp]; ok { + return p } return nil } diff --git a/vendor/github.com/99designs/gqlgen/codegen/config/config.go b/vendor/github.com/99designs/gqlgen/codegen/config/config.go index 1725adab..c7a7d4d8 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/config/config.go +++ b/vendor/github.com/99designs/gqlgen/codegen/config/config.go @@ -6,9 +6,12 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "sort" "strings" + "golang.org/x/tools/go/packages" + "github.com/99designs/gqlgen/internal/code" "github.com/pkg/errors" "github.com/vektah/gqlparser" @@ -17,12 +20,14 @@ import ( ) type Config struct { - SchemaFilename StringList `yaml:"schema,omitempty"` - Exec PackageConfig `yaml:"exec"` - Model PackageConfig `yaml:"model"` - Resolver PackageConfig `yaml:"resolver,omitempty"` - Models TypeMap `yaml:"models,omitempty"` - StructTag string `yaml:"struct_tag,omitempty"` + SchemaFilename StringList `yaml:"schema,omitempty"` + Exec PackageConfig `yaml:"exec"` + Model PackageConfig `yaml:"model"` + Resolver PackageConfig `yaml:"resolver,omitempty"` + AutoBind []string `yaml:"autobind"` + Models TypeMap `yaml:"models,omitempty"` + StructTag string `yaml:"struct_tag,omitempty"` + Directives map[string]DirectiveConfig `yaml:"directives,omitempty"` } var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"} @@ -33,6 +38,17 @@ func DefaultConfig() *Config { SchemaFilename: StringList{"schema.graphql"}, Model: PackageConfig{Filename: "models_gen.go"}, Exec: PackageConfig{Filename: "generated.go"}, + Directives: map[string]DirectiveConfig{ + "skip": { + SkipRuntime: true, + }, + "include": { + SkipRuntime: true, + }, + "deprecated": { + SkipRuntime: true, + }, + }, } } @@ -51,6 +67,13 @@ func LoadConfigFromDefaultLocations() (*Config, error) { return LoadConfig(cfgFile) } +var path2regex = strings.NewReplacer( + `.`, `\.`, + `*`, `.+`, + `\`, `[\\/]`, + `/`, `[\\/]`, +) + // LoadConfig reads the gqlgen.yml config file func LoadConfig(filename string) (*Config, error) { config := DefaultConfig() @@ -67,9 +90,35 @@ func LoadConfig(filename string) (*Config, error) { preGlobbing := config.SchemaFilename config.SchemaFilename = StringList{} for _, f := range preGlobbing { - matches, err := filepath.Glob(f) - if err != nil { - return nil, errors.Wrapf(err, "failed to glob schema filename %s", f) + var matches []string + + // for ** we want to override default globbing patterns and walk all + // subdirectories to match schema files. + if strings.Contains(f, "**") { + pathParts := strings.SplitN(f, "**", 2) + rest := strings.TrimPrefix(strings.TrimPrefix(pathParts[1], `\`), `/`) + // turn the rest of the glob into a regex, anchored only at the end because ** allows + // for any number of dirs in between and walk will let us match against the full path name + globRe := regexp.MustCompile(path2regex.Replace(rest) + `$`) + + if err := filepath.Walk(pathParts[0], func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if globRe.MatchString(strings.TrimPrefix(path, pathParts[0])) { + matches = append(matches, path) + } + + return nil + }); err != nil { + return nil, errors.Wrapf(err, "failed to walk schema at root %s", pathParts[0]) + } + } else { + matches, err = filepath.Glob(f) + if err != nil { + return nil, errors.Wrapf(err, "failed to glob schema filename %s", f) + } } for _, m := range matches { @@ -265,6 +314,10 @@ func (tm TypeMap) Add(Name string, goType string) { tm[Name] = modelCfg } +type DirectiveConfig struct { + SkipRuntime bool `yaml:"skip_runtime"` +} + func inStrSlice(haystack []string, needle string) bool { for _, v := range haystack { if needle == v { @@ -329,6 +382,31 @@ func (c *Config) normalize() error { return nil } +func (c *Config) Autobind(s *ast.Schema) error { + if len(c.AutoBind) == 0 { + return nil + } + ps, err := packages.Load(&packages.Config{Mode: packages.LoadTypes}, c.AutoBind...) + if err != nil { + return err + } + + for _, t := range s.Types { + if c.Models.UserDefined(t.Name) { + continue + } + + for _, p := range ps { + if t := p.Types.Scope().Lookup(t.Name); t != nil { + c.Models.Add(t.Name(), t.Pkg().Path()+"."+t.Name()) + break + } + } + } + + return nil +} + func (c *Config) InjectBuiltins(s *ast.Schema) { builtins := TypeMap{ "__Directive": {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Directive"}}, diff --git a/vendor/github.com/99designs/gqlgen/codegen/data.go b/vendor/github.com/99designs/gqlgen/codegen/data.go index f2ea70b4..98d2ec88 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/data.go +++ b/vendor/github.com/99designs/gqlgen/codegen/data.go @@ -15,7 +15,7 @@ type Data struct { Config *config.Config Schema *ast.Schema SchemaStr map[string]string - Directives map[string]*Directive + Directives DirectiveList Objects Objects Inputs Objects Interfaces map[string]*Interface @@ -51,6 +51,11 @@ func BuildData(cfg *config.Config) (*Data, error) { return nil, err } + err = cfg.Autobind(b.Schema) + if err != nil { + return nil, err + } + cfg.InjectBuiltins(b.Schema) b.Binder, err = b.Config.NewBinder(b.Schema) diff --git a/vendor/github.com/99designs/gqlgen/codegen/directive.go b/vendor/github.com/99designs/gqlgen/codegen/directive.go index 5a27e8ac..491f92f4 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/directive.go +++ b/vendor/github.com/99designs/gqlgen/codegen/directive.go @@ -10,12 +10,43 @@ import ( "github.com/vektah/gqlparser/ast" ) +type DirectiveList map[string]*Directive + +//LocationDirectives filter directives by location +func (dl DirectiveList) LocationDirectives(location string) DirectiveList { + return locationDirectives(dl, ast.DirectiveLocation(location)) +} + type Directive struct { + *ast.DirectiveDefinition Name string Args []*FieldArgument Builtin bool } +//IsLocation check location directive +func (d *Directive) IsLocation(location ...ast.DirectiveLocation) bool { + for _, l := range d.Locations { + for _, a := range location { + if l == a { + return true + } + } + } + + return false +} + +func locationDirectives(directives DirectiveList, location ...ast.DirectiveLocation) map[string]*Directive { + mDirectives := make(map[string]*Directive) + for name, d := range directives { + if d.IsLocation(location...) { + mDirectives[name] = d + } + } + return mDirectives +} + func (b *builder) buildDirectives() (map[string]*Directive, error) { directives := make(map[string]*Directive, len(b.Schema.Directives)) @@ -24,11 +55,6 @@ func (b *builder) buildDirectives() (map[string]*Directive, error) { return nil, errors.Errorf("directive with name %s already exists", name) } - var builtin bool - if name == "skip" || name == "include" || name == "deprecated" { - builtin = true - } - var args []*FieldArgument for _, arg := range dir.Arguments { tr, err := b.Binder.TypeReference(arg.Type, nil) @@ -53,9 +79,10 @@ func (b *builder) buildDirectives() (map[string]*Directive, error) { } directives[name] = &Directive{ - Name: name, - Args: args, - Builtin: builtin, + DirectiveDefinition: dir, + Name: name, + Args: args, + Builtin: b.Config.Directives[name].SkipRuntime, } } @@ -92,8 +119,10 @@ func (b *builder) getDirectives(list ast.DirectiveList) ([]*Directive, error) { }) } dirs[i] = &Directive{ - Name: d.Name, - Args: args, + Name: d.Name, + Args: args, + DirectiveDefinition: list[i].Definition, + Builtin: b.Config.Directives[d.Name].SkipRuntime, } } @@ -119,18 +148,12 @@ func (d *Directive) CallArgs() string { return strings.Join(args, ", ") } -func (d *Directive) ResolveArgs(obj string, next string) string { - args := []string{"ctx", obj, next} +func (d *Directive) ResolveArgs(obj string, next int) string { + args := []string{"ctx", obj, fmt.Sprintf("directive%d", next)} for _, arg := range d.Args { - dArg := "&" + arg.VarName - if !arg.TypeReference.IsPtr() { - if arg.Value != nil { - dArg = templates.Dump(arg.Value) - } else { - dArg = templates.Dump(arg.Default) - } - } else if arg.Value == nil && arg.Default == nil { + dArg := arg.VarName + if arg.Value == nil && arg.Default == nil { dArg = "nil" } diff --git a/vendor/github.com/99designs/gqlgen/codegen/directives.gotpl b/vendor/github.com/99designs/gqlgen/codegen/directives.gotpl new file mode 100644 index 00000000..67a0783e --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/directives.gotpl @@ -0,0 +1,137 @@ +{{ define "implDirectives" }}{{ $in := .DirectiveObjName }} + {{- range $i, $directive := .ImplDirectives -}} + directive{{add $i 1}} := func(ctx context.Context) (interface{}, error) { + {{- range $arg := $directive.Args }} + {{- if notNil "Value" $arg }} + {{ $arg.VarName }}, err := ec.{{ $arg.TypeReference.UnmarshalFunc }}(ctx, {{ $arg.Value | dump }}) + if err != nil{ + return nil, err + } + {{- else if notNil "Default" $arg }} + {{ $arg.VarName }}, err := ec.{{ $arg.TypeReference.UnmarshalFunc }}(ctx, {{ $arg.Default | dump }}) + if err != nil{ + return nil, err + } + {{- end }} + {{- end }} + return ec.directives.{{$directive.Name|ucFirst}}({{$directive.ResolveArgs $in $i }}) + } + {{- end -}} +{{ end }} + +{{define "queryDirectives"}} + for _, d := range obj.Directives { + switch d.Name { + {{- range $directive := . }} + case "{{$directive.Name}}": + {{- if $directive.Args }} + rawArgs := d.ArgumentMap(ec.Variables) + args, err := ec.{{ $directive.ArgsFunc }}(ctx,rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + {{- end }} + n := next + next = func(ctx context.Context) (interface{}, error) { + return ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}}) + } + {{- end }} + } + } + tmp, err := next(ctx) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if data, ok := tmp.(graphql.Marshaler); ok { + return data + } + ec.Errorf(ctx, `unexpected type %T from directive, should be graphql.Marshaler`, tmp) + return graphql.Null +{{end}} + +{{ if .Directives.LocationDirectives "QUERY" }} +func (ec *executionContext) _queryMiddleware(ctx context.Context, obj *ast.OperationDefinition, next func(ctx context.Context) (interface{}, error)) graphql.Marshaler { + {{ template "queryDirectives" .Directives.LocationDirectives "QUERY" }} +} +{{ end }} + +{{ if .Directives.LocationDirectives "MUTATION" }} +func (ec *executionContext) _mutationMiddleware(ctx context.Context, obj *ast.OperationDefinition, next func(ctx context.Context) (interface{}, error)) graphql.Marshaler { + {{ template "queryDirectives" .Directives.LocationDirectives "MUTATION" }} +} +{{ end }} + +{{ if .Directives.LocationDirectives "SUBSCRIPTION" }} +func (ec *executionContext) _subscriptionMiddleware(ctx context.Context, obj *ast.OperationDefinition, next func(ctx context.Context) (interface{}, error)) func() graphql.Marshaler { + for _, d := range obj.Directives { + switch d.Name { + {{- range $directive := .Directives.LocationDirectives "SUBSCRIPTION" }} + case "{{$directive.Name}}": + {{- if $directive.Args }} + rawArgs := d.ArgumentMap(ec.Variables) + args, err := ec.{{ $directive.ArgsFunc }}(ctx,rawArgs) + if err != nil { + ec.Error(ctx, err) + return func() graphql.Marshaler { + return graphql.Null + } + } + {{- end }} + n := next + next = func(ctx context.Context) (interface{}, error) { + return ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}}) + } + {{- end }} + } + } + tmp, err := next(ctx) + if err != nil { + ec.Error(ctx, err) + return func() graphql.Marshaler { + return graphql.Null + } + } + if data, ok := tmp.(func() graphql.Marshaler); ok { + return data + } + ec.Errorf(ctx, `unexpected type %T from directive, should be graphql.Marshaler`, tmp) + return func() graphql.Marshaler { + return graphql.Null + } +} +{{ end }} + +{{ if .Directives.LocationDirectives "FIELD" }} + func (ec *executionContext) _fieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) interface{} { + {{- if .Directives.LocationDirectives "FIELD" }} + rctx := graphql.GetResolverContext(ctx) + for _, d := range rctx.Field.Directives { + switch d.Name { + {{- range $directive := .Directives.LocationDirectives "FIELD" }} + case "{{$directive.Name}}": + {{- if $directive.Args }} + rawArgs := d.ArgumentMap(ec.Variables) + args, err := ec.{{ $directive.ArgsFunc }}(ctx,rawArgs) + if err != nil { + ec.Error(ctx, err) + return nil + } + {{- end }} + n := next + next = func(ctx context.Context) (interface{}, error) { + return ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}}) + } + {{- end }} + } + } + {{- end }} + res, err := ec.ResolverMiddleware(ctx, next) + if err != nil { + ec.Error(ctx, err) + return nil + } + return res + } +{{ end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/field.go b/vendor/github.com/99designs/gqlgen/codegen/field.go index f5f7b221..264b59ce 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/field.go +++ b/vendor/github.com/99designs/gqlgen/codegen/field.go @@ -284,7 +284,28 @@ func (b *builder) findBindStructTarget(strukt *types.Struct, name string) (types } func (f *Field) HasDirectives() bool { - return len(f.Directives) > 0 + return len(f.ImplDirectives()) > 0 +} + +func (f *Field) DirectiveObjName() string { + if f.Object.Root { + return "nil" + } + return f.GoReceiverName +} + +func (f *Field) ImplDirectives() []*Directive { + var d []*Directive + loc := ast.LocationFieldDefinition + if f.Object.IsInputType() { + loc = ast.LocationInputFieldDefinition + } + for i := range f.Directives { + if !f.Directives[i].Builtin && f.Directives[i].IsLocation(loc) { + d = append(d, f.Directives[i]) + } + } + return d } func (f *Field) IsReserved() bool { diff --git a/vendor/github.com/99designs/gqlgen/codegen/field.gotpl b/vendor/github.com/99designs/gqlgen/codegen/field.gotpl index 9718a08a..c0f6fcae 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/field.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/field.gotpl @@ -37,9 +37,15 @@ } } {{ else }} - func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Context, field graphql.CollectedField{{ if not $object.Root }}, obj {{$object.Reference | ref}}{{end}}) graphql.Marshaler { + func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Context, field graphql.CollectedField{{ if not $object.Root }}, obj {{$object.Reference | ref}}{{end}}) (ret graphql.Marshaler) { ctx = ec.Tracer.StartFieldExecution(ctx, field) - defer func () { ec.Tracer.EndFieldExecution(ctx) }() + defer func () { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + ec.Tracer.EndFieldExecution(ctx) + }() rctx := &graphql.ResolverContext{ Object: {{$object.Name|quote}}, Field: field, @@ -57,31 +63,19 @@ rctx.Args = args {{- end }} ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) - resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - {{- if $field.IsResolver }} - return ec.resolvers.{{ $field.ShortInvocation }} - {{- else if $field.IsMap }} - switch v := {{$field.GoReceiverName}}[{{$field.Name|quote}}].(type) { - case {{$field.TypeReference.GO | ref}}: - return v, nil - case {{$field.TypeReference.Elem.GO | ref}}: - return &v, nil - case nil: - return ({{$field.TypeReference.GO | ref}})(nil), nil - default: - return nil, fmt.Errorf("unexpected type %T for field %s", v, {{ $field.Name | quote}}) - } - {{- else if $field.IsMethod }} - {{- if $field.NoErr }} - return {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}), nil - {{- else }} - return {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}) - {{- end }} - {{- else if $field.IsVariable }} - return {{$field.GoReceiverName}}.{{$field.GoFieldName}}, nil - {{- end }} - }) + {{- if $.Directives.LocationDirectives "FIELD" }} + resTmp := ec._fieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) { + {{ template "field" $field }} + }) + {{ else }} + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + {{ template "field" $field }} + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + {{- end }} if resTmp == nil { {{- if $field.TypeReference.GQL.NonNull }} if !ec.HasError(rctx) { @@ -98,3 +92,49 @@ {{ end }} {{- end }}{{- end}} + +{{ define "field" }} + {{- if .HasDirectives -}} + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + {{ template "fieldDefinition" . }} + } + {{ template "implDirectives" . }} + tmp, err := directive{{.ImplDirectives|len}}(rctx) + if err != nil { + return nil, err + } + if data, ok := tmp.({{ .TypeReference.GO | ref }}) ; ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be {{ .TypeReference.GO }}`, tmp) + {{- else -}} + ctx = rctx // use context from middleware stack in children + {{ template "fieldDefinition" . }} + {{- end -}} +{{ end }} + +{{ define "fieldDefinition" }} + {{- if .IsResolver -}} + return ec.resolvers.{{ .ShortInvocation }} + {{- else if .IsMap -}} + switch v := {{.GoReceiverName}}[{{.Name|quote}}].(type) { + case {{.TypeReference.GO | ref}}: + return v, nil + case {{.TypeReference.Elem.GO | ref}}: + return &v, nil + case nil: + return ({{.TypeReference.GO | ref}})(nil), nil + default: + return nil, fmt.Errorf("unexpected type %T for field %s", v, {{ .Name | quote}}) + } + {{- else if .IsMethod -}} + {{- if .NoErr -}} + return {{.GoReceiverName}}.{{.GoFieldName}}({{ .CallArgs }}), nil + {{- else -}} + return {{.GoReceiverName}}.{{.GoFieldName}}({{ .CallArgs }}) + {{- end -}} + {{- else if .IsVariable -}} + return {{.GoReceiverName}}.{{.GoFieldName}}, nil + {{- end }} +{{- end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/generated!.gotpl b/vendor/github.com/99designs/gqlgen/codegen/generated!.gotpl index 5753f1d1..a95e57b6 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/generated!.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/generated!.gotpl @@ -117,7 +117,13 @@ func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinitio ec := executionContext{graphql.GetRequestContext(ctx), e} buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + {{ if .Directives.LocationDirectives "QUERY" -}} + data := ec._queryMiddleware(ctx, op, func(ctx context.Context) (interface{}, error){ + return ec._{{.QueryRoot.Name}}(ctx, op.SelectionSet), nil + }) + {{- else -}} data := ec._{{.QueryRoot.Name}}(ctx, op.SelectionSet) + {{- end }} var buf bytes.Buffer data.MarshalGQL(&buf) return buf.Bytes() @@ -138,7 +144,13 @@ func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefini ec := executionContext{graphql.GetRequestContext(ctx), e} buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + {{ if .Directives.LocationDirectives "MUTATION" -}} + data := ec._mutationMiddleware(ctx, op, func(ctx context.Context) (interface{}, error){ + return ec._{{.MutationRoot.Name}}(ctx, op.SelectionSet), nil + }) + {{- else -}} data := ec._{{.MutationRoot.Name}}(ctx, op.SelectionSet) + {{- end }} var buf bytes.Buffer data.MarshalGQL(&buf) return buf.Bytes() @@ -158,7 +170,13 @@ func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDe {{- if .SubscriptionRoot }} ec := executionContext{graphql.GetRequestContext(ctx), e} - next := ec._{{.SubscriptionRoot.Name}}(ctx, op.SelectionSet) + {{ if .Directives.LocationDirectives "SUBSCRIPTION" -}} + next := ec._subscriptionMiddleware(ctx, op, func(ctx context.Context) (interface{}, error){ + return ec._{{.SubscriptionRoot.Name}}(ctx, op.SelectionSet),nil + }) + {{- else -}} + next := ec._{{.SubscriptionRoot.Name}}(ctx, op.SelectionSet) + {{- end }} if ec.Errors != nil { return graphql.OneShot(&graphql.Response{Data: []byte("null"), Errors: ec.Errors}) } @@ -196,45 +214,6 @@ type executionContext struct { *executableSchema } -func (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) (ret interface{}) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - {{- if .Directives }} - rctx := graphql.GetResolverContext(ctx) - for _, d := range rctx.Field.Definition.Directives { - switch d.Name { - {{- range $directive := .Directives }} - case "{{$directive.Name}}": - if ec.directives.{{$directive.Name|ucFirst}} != nil { - {{- if $directive.Args }} - rawArgs := d.ArgumentMap(ec.Variables) - args, err := ec.{{ $directive.ArgsFunc }}(ctx,rawArgs) - if err != nil { - ec.Error(ctx, err) - return nil - } - {{- end }} - n := next - next = func(ctx context.Context) (interface{}, error) { - return ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}}) - } - } - {{- end }} - } - } - {{- end }} - res, err := ec.ResolverMiddleware(ctx, next) - if err != nil { - ec.Error(ctx, err) - return nil - } - return res -} - func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") diff --git a/vendor/github.com/99designs/gqlgen/codegen/input.gotpl b/vendor/github.com/99designs/gqlgen/codegen/input.gotpl index c8ac7ad3..b51d53a2 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/input.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/input.gotpl @@ -1,8 +1,8 @@ {{- range $input := .Inputs }} {{- if not .HasUnmarshal }} - func (ec *executionContext) unmarshalInput{{ .Name }}(ctx context.Context, v interface{}) ({{.Type | ref}}, error) { + func (ec *executionContext) unmarshalInput{{ .Name }}(ctx context.Context, obj interface{}) ({{.Type | ref}}, error) { var it {{.Type | ref}} - var asMap = v.(map[string]interface{}) + var asMap = obj.(map[string]interface{}) {{ range $field := .Fields}} {{- if $field.Default}} if _, present := asMap[{{$field.Name|quote}}] ; !present { @@ -16,22 +16,10 @@ {{- range $field := .Fields }} case {{$field.Name|quote}}: var err error - {{- if $field.Directives }} - getField0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v) } - - {{- range $i, $directive := $field.Directives }} - getField{{add $i 1}} := func(ctx context.Context) (res interface{}, err error) { - {{- range $dArg := $directive.Args }} - {{- if and $dArg.TypeReference.IsPtr ( notNil "Value" $dArg ) }} - {{ $dArg.VarName }} := {{ $dArg.Value | dump }} - {{- end }} - {{- end }} - n := getField{{$i}} - return ec.directives.{{$directive.Name|ucFirst}}({{$directive.ResolveArgs "it" "n" }}) - } - {{- end }} - - tmp, err := getField{{$field.Directives|len}}(ctx) + {{- if $field.ImplDirectives }} + directive0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v) } + {{ template "implDirectives" $field }} + tmp, err := directive{{$field.ImplDirectives|len}}(ctx) if err != nil { return it, err } diff --git a/vendor/github.com/99designs/gqlgen/graphql/version.go b/vendor/github.com/99designs/gqlgen/graphql/version.go index 11dc6b01..3c5e65d6 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/version.go +++ b/vendor/github.com/99designs/gqlgen/graphql/version.go @@ -1,3 +1,3 @@ package graphql -const Version = "v0.9.0" +const Version = "v0.9.1" diff --git a/vendor/github.com/99designs/gqlgen/handler/graphql.go b/vendor/github.com/99designs/gqlgen/handler/graphql.go index a2254222..289901f0 100644 --- a/vendor/github.com/99designs/gqlgen/handler/graphql.go +++ b/vendor/github.com/99designs/gqlgen/handler/graphql.go @@ -2,6 +2,8 @@ package handler import ( "context" + "crypto/sha256" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -28,8 +30,30 @@ type params struct { Query string `json:"query"` OperationName string `json:"operationName"` Variables map[string]interface{} `json:"variables"` + Extensions *extensions `json:"extensions"` } +type extensions struct { + PersistedQuery *persistedQuery `json:"persistedQuery"` +} + +type persistedQuery struct { + Sha256 string `json:"sha256Hash"` + Version int64 `json:"version"` +} + +const ( + errPersistedQueryNotSupported = "PersistedQueryNotSupported" + errPersistedQueryNotFound = "PersistedQueryNotFound" +) + +type PersistedQueryCache interface { + Add(ctx context.Context, hash string, query string) + Get(ctx context.Context, hash string) (string, bool) +} + +type websocketInitFunc func(ctx context.Context, initPayload InitPayload) error + type Config struct { cacheSize int upgrader websocket.Upgrader @@ -40,10 +64,12 @@ type Config struct { tracer graphql.Tracer complexityLimit int complexityLimitFunc graphql.ComplexityLimitFunc + websocketInitFunc websocketInitFunc disableIntrospection bool connectionKeepAlivePingInterval time.Duration uploadMaxMemory int64 uploadMaxSize int64 + apqCache PersistedQueryCache } func (c *Config) newRequestContext(es graphql.ExecutableSchema, doc *ast.QueryDocument, op *ast.OperationDefinition, query string, variables map[string]interface{}) *graphql.RequestContext { @@ -250,6 +276,14 @@ func (tw *tracerWrapper) EndOperationExecution(ctx context.Context) { tw.tracer1.EndOperationExecution(ctx) } +// WebsocketInitFunc is called when the server receives connection init message from the client. +// This can be used to check initial payload to see whether to accept the websocket connection. +func WebsocketInitFunc(websocketInitFunc func(ctx context.Context, initPayload InitPayload) error) Option { + return func(cfg *Config) { + cfg.websocketInitFunc = websocketInitFunc + } +} + // CacheSize sets the maximum size of the query cache. // If size is less than or equal to 0, the cache is disabled. func CacheSize(size int) Option { @@ -285,6 +319,13 @@ func WebsocketKeepAliveDuration(duration time.Duration) Option { } } +// Add cache that will hold queries for automatic persisted queries (APQ) +func EnablePersistedQueryCache(cache PersistedQueryCache) Option { + return func(cfg *Config) { + cfg.apqCache = cache + } +} + const DefaultCacheSize = 1000 const DefaultConnectionKeepAlivePingInterval = 25 * time.Second @@ -344,6 +385,11 @@ type graphqlHandler struct { exec graphql.ExecutableSchema } +func computeQueryHash(query string) string { + b := sha256.Sum256([]byte(query)) + return hex.EncodeToString(b[:]) +} + func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions { w.Header().Set("Allow", "OPTIONS, GET, POST") @@ -369,6 +415,13 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } } + + if extensions := r.URL.Query().Get("extensions"); extensions != "" { + if err := jsonDecode(strings.NewReader(extensions), &reqParams.Extensions); err != nil { + sendErrorf(w, http.StatusBadRequest, "extensions could not be decoded") + return + } + } case http.MethodPost: mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) if err != nil { @@ -409,6 +462,41 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() + var queryHash string + apqRegister := false + apq := reqParams.Extensions != nil && reqParams.Extensions.PersistedQuery != nil + if apq { + // client has enabled apq + queryHash = reqParams.Extensions.PersistedQuery.Sha256 + if gh.cfg.apqCache == nil { + // server has disabled apq + sendErrorf(w, http.StatusOK, errPersistedQueryNotSupported) + return + } + if reqParams.Extensions.PersistedQuery.Version != 1 { + sendErrorf(w, http.StatusOK, "Unsupported persisted query version") + return + } + if reqParams.Query == "" { + // client sent optimistic query hash without query string + query, ok := gh.cfg.apqCache.Get(ctx, queryHash) + if !ok { + sendErrorf(w, http.StatusOK, errPersistedQueryNotFound) + return + } + reqParams.Query = query + } else { + if computeQueryHash(reqParams.Query) != queryHash { + sendErrorf(w, http.StatusOK, "provided sha does not match query") + return + } + apqRegister = true + } + } else if reqParams.Query == "" { + sendErrorf(w, http.StatusUnprocessableEntity, "Must provide query string") + return + } + var doc *ast.QueryDocument var cacheHit bool if gh.cache != nil { @@ -463,6 +551,11 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + if apqRegister && gh.cfg.apqCache != nil { + // Add to persisted query cache + gh.cfg.apqCache.Add(ctx, queryHash, reqParams.Query) + } + switch op.Operation { case ast.Query: b, err := json.Marshal(gh.exec.Query(ctx, op)) diff --git a/vendor/github.com/99designs/gqlgen/handler/websocket.go b/vendor/github.com/99designs/gqlgen/handler/websocket.go index 58f38e5d..07a1a8c2 100644 --- a/vendor/github.com/99designs/gqlgen/handler/websocket.go +++ b/vendor/github.com/99designs/gqlgen/handler/websocket.go @@ -12,7 +12,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/gorilla/websocket" - "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru" "github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast" "github.com/vektah/gqlparser/gqlerror" @@ -94,6 +94,14 @@ func (c *wsConnection) init() bool { } } + if c.cfg.websocketInitFunc != nil { + if err := c.cfg.websocketInitFunc(c.ctx, c.initPayload); err != nil { + c.sendConnectionError(err.Error()) + c.close(websocket.CloseNormalClosure, "terminated") + return false + } + } + c.write(&operationMessage{Type: connectionAckMsg}) case connectionTerminateMsg: c.close(websocket.CloseNormalClosure, "terminated") diff --git a/vendor/github.com/99designs/gqlgen/plugin/modelgen/models.go b/vendor/github.com/99designs/gqlgen/plugin/modelgen/models.go index bb400f1b..376499ae 100644 --- a/vendor/github.com/99designs/gqlgen/plugin/modelgen/models.go +++ b/vendor/github.com/99designs/gqlgen/plugin/modelgen/models.go @@ -72,6 +72,11 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error { return err } + err = cfg.Autobind(schema) + if err != nil { + return err + } + cfg.InjectBuiltins(schema) binder, err := cfg.NewBinder(schema) diff --git a/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go b/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go index 00a6d5c9..6785c77c 100644 --- a/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go +++ b/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go @@ -19,6 +19,7 @@ type Plugin struct{} var _ plugin.CodeGenerator = &Plugin{} func (m *Plugin) Name() string { + // TODO: typo, should be resolvergen return "resovlergen" } func (m *Plugin) GenerateCode(data *codegen.Data) error { diff --git a/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.gotpl b/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.gotpl index 7d95e690..66d6efac 100644 --- a/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.gotpl +++ b/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.gotpl @@ -20,18 +20,18 @@ type {{.ResolverType}} struct {} {{ range $object := .Objects -}} {{- if $object.HasResolvers -}} func (r *{{$.ResolverType}}) {{$object.Name}}() {{ $object.ResolverInterface | ref }} { - return &{{lcFirst $object.Name}}Resolver{r} + return &{{lcFirst $object.Name}}{{ucFirst $.ResolverType}}{r} } {{ end -}} {{ end }} {{ range $object := .Objects -}} {{- if $object.HasResolvers -}} - type {{lcFirst $object.Name}}Resolver struct { *Resolver } + type {{lcFirst $object.Name}}{{ucFirst $.ResolverType}} struct { *{{$.ResolverType}} } {{ range $field := $object.Fields -}} {{- if $field.IsResolver -}} - func (r *{{lcFirst $object.Name}}Resolver) {{$field.GoFieldName}}{{ $field.ShortResolverDeclaration }} { + func (r *{{lcFirst $object.Name}}{{ucFirst $.ResolverType}}) {{$field.GoFieldName}}{{ $field.ShortResolverDeclaration }} { panic("not implemented") } {{ end -}} diff --git a/vendor/github.com/99designs/gqlgen/plugin/schemaconfig/schemaconfig.go b/vendor/github.com/99designs/gqlgen/plugin/schemaconfig/schemaconfig.go new file mode 100644 index 00000000..1fcf4105 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/plugin/schemaconfig/schemaconfig.go @@ -0,0 +1,93 @@ +package schemaconfig + +import ( + "github.com/99designs/gqlgen/codegen/config" + "github.com/99designs/gqlgen/plugin" + "github.com/vektah/gqlparser/ast" +) + +func New() plugin.Plugin { + return &Plugin{} +} + +type Plugin struct{} + +var _ plugin.ConfigMutator = &Plugin{} + +func (m *Plugin) Name() string { + return "schemaconfig" +} + +func (m *Plugin) MutateConfig(cfg *config.Config) error { + if err := cfg.Check(); err != nil { + return err + } + + schema, _, err := cfg.LoadSchema() + if err != nil { + return err + } + + cfg.Directives["goModel"] = config.DirectiveConfig{ + SkipRuntime: true, + } + + cfg.Directives["goField"] = config.DirectiveConfig{ + SkipRuntime: true, + } + + for _, schemaType := range schema.Types { + if schemaType == schema.Query || schemaType == schema.Mutation || schemaType == schema.Subscription { + continue + } + + if bd := schemaType.Directives.ForName("goModel"); bd != nil { + if ma := bd.Arguments.ForName("model"); ma != nil { + if mv, err := ma.Value.Value(nil); err == nil { + cfg.Models.Add(schemaType.Name, mv.(string)) + } + } + if ma := bd.Arguments.ForName("models"); ma != nil { + if mvs, err := ma.Value.Value(nil); err == nil { + for _, mv := range mvs.([]interface{}) { + cfg.Models.Add(schemaType.Name, mv.(string)) + } + } + } + } + + if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject { + for _, field := range schemaType.Fields { + if fd := field.Directives.ForName("goField"); fd != nil { + forceResolver := cfg.Models[schemaType.Name].Fields[field.Name].Resolver + fieldName := cfg.Models[schemaType.Name].Fields[field.Name].FieldName + + if ra := fd.Arguments.ForName("forceResolver"); ra != nil { + if fr, err := ra.Value.Value(nil); err == nil { + forceResolver = fr.(bool) + } + } + + if na := fd.Arguments.ForName("name"); na != nil { + if fr, err := na.Value.Value(nil); err == nil { + fieldName = fr.(string) + } + } + + if cfg.Models[schemaType.Name].Fields == nil { + cfg.Models[schemaType.Name] = config.TypeMapEntry{ + Model: cfg.Models[schemaType.Name].Model, + Fields: map[string]config.TypeMapField{}, + } + } + + cfg.Models[schemaType.Name].Fields[field.Name] = config.TypeMapField{ + FieldName: fieldName, + Resolver: forceResolver, + } + } + } + } + } + return nil +} diff --git a/vendor/github.com/blang/semver/.travis.yml b/vendor/github.com/blang/semver/.travis.yml new file mode 100644 index 00000000..7319bdea --- /dev/null +++ b/vendor/github.com/blang/semver/.travis.yml @@ -0,0 +1,25 @@ +language: go +matrix: + include: + - go: 1.4.x + - go: 1.5.x + - go: 1.6.x + - go: 1.7.x + - go: 1.8.x + - go: 1.9.x + - go: 1.10.x + - go: 1.11.x + - go: tip + allow_failures: + - go: tip +install: +- go get golang.org/x/tools/cmd/cover +- go get github.com/mattn/goveralls +script: +- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci + -repotoken=$COVERALLS_TOKEN +- echo "Build examples" ; cd examples && go build +- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .) +env: + global: + secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw= diff --git a/vendor/github.com/blang/semver/LICENSE b/vendor/github.com/blang/semver/LICENSE new file mode 100644 index 00000000..5ba5c86f --- /dev/null +++ b/vendor/github.com/blang/semver/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Benedikt Lang <github at benediktlang.de> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/github.com/blang/semver/README.md b/vendor/github.com/blang/semver/README.md new file mode 100644 index 00000000..e05f9865 --- /dev/null +++ b/vendor/github.com/blang/semver/README.md @@ -0,0 +1,194 @@ +semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.svg)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/blang/semver)](https://goreportcard.com/report/github.com/blang/semver) +====== + +semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`. + +Usage +----- +```bash +$ go get github.com/blang/semver +``` +Note: Always vendor your dependencies or fix on a specific version tag. + +```go +import github.com/blang/semver +v1, err := semver.Make("1.0.0-beta") +v2, err := semver.Make("2.0.0-beta") +v1.Compare(v2) +``` + +Also check the [GoDocs](http://godoc.org/github.com/blang/semver). + +Why should I use this lib? +----- + +- Fully spec compatible +- No reflection +- No regex +- Fully tested (Coverage >99%) +- Readable parsing/validation errors +- Fast (See [Benchmarks](#benchmarks)) +- Only Stdlib +- Uses values instead of pointers +- Many features, see below + + +Features +----- + +- Parsing and validation at all levels +- Comparator-like comparisons +- Compare Helper Methods +- InPlace manipulation +- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1` +- Wildcards `>=1.x`, `<=2.5.x` +- Sortable (implements sort.Interface) +- database/sql compatible (sql.Scanner/Valuer) +- encoding/json compatible (json.Marshaler/Unmarshaler) + +Ranges +------ + +A `Range` is a set of conditions which specify which versions satisfy the range. + +A condition is composed of an operator and a version. The supported operators are: + +- `<1.0.0` Less than `1.0.0` +- `<=1.0.0` Less than or equal to `1.0.0` +- `>1.0.0` Greater than `1.0.0` +- `>=1.0.0` Greater than or equal to `1.0.0` +- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0` +- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`. + +Note that spaces between the operator and the version will be gracefully tolerated. + +A `Range` can link multiple `Ranges` separated by space: + +Ranges can be linked by logical AND: + + - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0` + - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2` + +Ranges can also be linked by logical OR: + + - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x` + +AND has a higher precedence than OR. It's not possible to use brackets. + +Ranges can be combined by both AND and OR + + - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` + +Range usage: + +``` +v, err := semver.Parse("1.2.3") +expectedRange, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0") +if expectedRange(v) { + //valid +} + +``` + +Example +----- + +Have a look at full examples in [examples/main.go](examples/main.go) + +```go +import github.com/blang/semver + +v, err := semver.Make("0.0.1-alpha.preview+123.github") +fmt.Printf("Major: %d\n", v.Major) +fmt.Printf("Minor: %d\n", v.Minor) +fmt.Printf("Patch: %d\n", v.Patch) +fmt.Printf("Pre: %s\n", v.Pre) +fmt.Printf("Build: %s\n", v.Build) + +// Prerelease versions array +if len(v.Pre) > 0 { + fmt.Println("Prerelease versions:") + for i, pre := range v.Pre { + fmt.Printf("%d: %q\n", i, pre) + } +} + +// Build meta data array +if len(v.Build) > 0 { + fmt.Println("Build meta data:") + for i, build := range v.Build { + fmt.Printf("%d: %q\n", i, build) + } +} + +v001, err := semver.Make("0.0.1") +// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE +v001.GT(v) == true +v.LT(v001) == true +v.GTE(v) == true +v.LTE(v) == true + +// Or use v.Compare(v2) for comparisons (-1, 0, 1): +v001.Compare(v) == 1 +v.Compare(v001) == -1 +v.Compare(v) == 0 + +// Manipulate Version in place: +v.Pre[0], err = semver.NewPRVersion("beta") +if err != nil { + fmt.Printf("Error parsing pre release version: %q", err) +} + +fmt.Println("\nValidate versions:") +v.Build[0] = "?" + +err = v.Validate() +if err != nil { + fmt.Printf("Validation failed: %s\n", err) +} +``` + + +Benchmarks +----- + + BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op + BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op + BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op + BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op + BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op + BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op + BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op + BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op + BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op + BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op + BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op + BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op + BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op + BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op + BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op + BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op + +See benchmark cases at [semver_test.go](semver_test.go) + + +Motivation +----- + +I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like. + + +Contribution +----- + +Feel free to make a pull request. For bigger changes create a issue first to discuss about it. + + +License +----- + +See [LICENSE](LICENSE) file. diff --git a/vendor/github.com/blang/semver/go.mod b/vendor/github.com/blang/semver/go.mod new file mode 100644 index 00000000..d0083058 --- /dev/null +++ b/vendor/github.com/blang/semver/go.mod @@ -0,0 +1 @@ +module github.com/blang/semver diff --git a/vendor/github.com/blang/semver/json.go b/vendor/github.com/blang/semver/json.go new file mode 100644 index 00000000..a74bf7c4 --- /dev/null +++ b/vendor/github.com/blang/semver/json.go @@ -0,0 +1,23 @@ +package semver + +import ( + "encoding/json" +) + +// MarshalJSON implements the encoding/json.Marshaler interface. +func (v Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements the encoding/json.Unmarshaler interface. +func (v *Version) UnmarshalJSON(data []byte) (err error) { + var versionString string + + if err = json.Unmarshal(data, &versionString); err != nil { + return + } + + *v, err = Parse(versionString) + + return +} diff --git a/vendor/github.com/blang/semver/package.json b/vendor/github.com/blang/semver/package.json new file mode 100644 index 00000000..1cf8ebdd --- /dev/null +++ b/vendor/github.com/blang/semver/package.json @@ -0,0 +1,17 @@ +{ + "author": "blang", + "bugs": { + "URL": "https://github.com/blang/semver/issues", + "url": "https://github.com/blang/semver/issues" + }, + "gx": { + "dvcsimport": "github.com/blang/semver" + }, + "gxVersion": "0.10.0", + "language": "go", + "license": "MIT", + "name": "semver", + "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", + "version": "3.5.1" +} + diff --git a/vendor/github.com/blang/semver/range.go b/vendor/github.com/blang/semver/range.go new file mode 100644 index 00000000..95f7139b --- /dev/null +++ b/vendor/github.com/blang/semver/range.go @@ -0,0 +1,416 @@ +package semver + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +type wildcardType int + +const ( + noneWildcard wildcardType = iota + majorWildcard wildcardType = 1 + minorWildcard wildcardType = 2 + patchWildcard wildcardType = 3 +) + +func wildcardTypefromInt(i int) wildcardType { + switch i { + case 1: + return majorWildcard + case 2: + return minorWildcard + case 3: + return patchWildcard + default: + return noneWildcard + } +} + +type comparator func(Version, Version) bool + +var ( + compEQ comparator = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 0 + } + compNE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) != 0 + } + compGT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 1 + } + compGE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) >= 0 + } + compLT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == -1 + } + compLE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) <= 0 + } +) + +type versionRange struct { + v Version + c comparator +} + +// rangeFunc creates a Range from the given versionRange. +func (vr *versionRange) rangeFunc() Range { + return Range(func(v Version) bool { + return vr.c(v, vr.v) + }) +} + +// Range represents a range of versions. +// A Range can be used to check if a Version satisfies it: +// +// range, err := semver.ParseRange(">1.0.0 <2.0.0") +// range(semver.MustParse("1.1.1") // returns true +type Range func(Version) bool + +// OR combines the existing Range with another Range using logical OR. +func (rf Range) OR(f Range) Range { + return Range(func(v Version) bool { + return rf(v) || f(v) + }) +} + +// AND combines the existing Range with another Range using logical AND. +func (rf Range) AND(f Range) Range { + return Range(func(v Version) bool { + return rf(v) && f(v) + }) +} + +// ParseRange parses a range and returns a Range. +// If the range could not be parsed an error is returned. +// +// Valid ranges are: +// - "<1.0.0" +// - "<=1.0.0" +// - ">1.0.0" +// - ">=1.0.0" +// - "1.0.0", "=1.0.0", "==1.0.0" +// - "!1.0.0", "!=1.0.0" +// +// A Range can consist of multiple ranges separated by space: +// Ranges can be linked by logical AND: +// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0" +// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2 +// +// Ranges can also be linked by logical OR: +// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x" +// +// AND has a higher precedence than OR. It's not possible to use brackets. +// +// Ranges can be combined by both AND and OR +// +// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` +func ParseRange(s string) (Range, error) { + parts := splitAndTrim(s) + orParts, err := splitORParts(parts) + if err != nil { + return nil, err + } + expandedParts, err := expandWildcardVersion(orParts) + if err != nil { + return nil, err + } + var orFn Range + for _, p := range expandedParts { + var andFn Range + for _, ap := range p { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + vr, err := buildVersionRange(opStr, vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) + } + rf := vr.rangeFunc() + + // Set function + if andFn == nil { + andFn = rf + } else { // Combine with existing function + andFn = andFn.AND(rf) + } + } + if orFn == nil { + orFn = andFn + } else { + orFn = orFn.OR(andFn) + } + + } + return orFn, nil +} + +// splitORParts splits the already cleaned parts by '||'. +// Checks for invalid positions of the operator and returns an +// error if found. +func splitORParts(parts []string) ([][]string, error) { + var ORparts [][]string + last := 0 + for i, p := range parts { + if p == "||" { + if i == 0 { + return nil, fmt.Errorf("First element in range is '||'") + } + ORparts = append(ORparts, parts[last:i]) + last = i + 1 + } + } + if last == len(parts) { + return nil, fmt.Errorf("Last element in range is '||'") + } + ORparts = append(ORparts, parts[last:]) + return ORparts, nil +} + +// buildVersionRange takes a slice of 2: operator and version +// and builds a versionRange, otherwise an error. +func buildVersionRange(opStr, vStr string) (*versionRange, error) { + c := parseComparator(opStr) + if c == nil { + return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) + } + v, err := Parse(vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) + } + + return &versionRange{ + v: v, + c: c, + }, nil + +} + +// inArray checks if a byte is contained in an array of bytes +func inArray(s byte, list []byte) bool { + for _, el := range list { + if el == s { + return true + } + } + return false +} + +// splitAndTrim splits a range string by spaces and cleans whitespaces +func splitAndTrim(s string) (result []string) { + last := 0 + var lastChar byte + excludeFromSplit := []byte{'>', '<', '='} + for i := 0; i < len(s); i++ { + if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) { + if last < i-1 { + result = append(result, s[last:i]) + } + last = i + 1 + } else if s[i] != ' ' { + lastChar = s[i] + } + } + if last < len(s)-1 { + result = append(result, s[last:]) + } + + for i, v := range result { + result[i] = strings.Replace(v, " ", "", -1) + } + + // parts := strings.Split(s, " ") + // for _, x := range parts { + // if s := strings.TrimSpace(x); len(s) != 0 { + // result = append(result, s) + // } + // } + return +} + +// splitComparatorVersion splits the comparator from the version. +// Input must be free of leading or trailing spaces. +func splitComparatorVersion(s string) (string, string, error) { + i := strings.IndexFunc(s, unicode.IsDigit) + if i == -1 { + return "", "", fmt.Errorf("Could not get version from string: %q", s) + } + return strings.TrimSpace(s[0:i]), s[i:], nil +} + +// getWildcardType will return the type of wildcard that the +// passed version contains +func getWildcardType(vStr string) wildcardType { + parts := strings.Split(vStr, ".") + nparts := len(parts) + wildcard := parts[nparts-1] + + possibleWildcardType := wildcardTypefromInt(nparts) + if wildcard == "x" { + return possibleWildcardType + } + + return noneWildcard +} + +// createVersionFromWildcard will convert a wildcard version +// into a regular version, replacing 'x's with '0's, handling +// special cases like '1.x.x' and '1.x' +func createVersionFromWildcard(vStr string) string { + // handle 1.x.x + vStr2 := strings.Replace(vStr, ".x.x", ".x", 1) + vStr2 = strings.Replace(vStr2, ".x", ".0", 1) + parts := strings.Split(vStr2, ".") + + // handle 1.x + if len(parts) == 2 { + return vStr2 + ".0" + } + + return vStr2 +} + +// incrementMajorVersion will increment the major version +// of the passed version +func incrementMajorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + parts[0] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// incrementMajorVersion will increment the minor version +// of the passed version +func incrementMinorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[1]) + if err != nil { + return "", err + } + parts[1] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// expandWildcardVersion will expand wildcards inside versions +// following these rules: +// +// * when dealing with patch wildcards: +// >= 1.2.x will become >= 1.2.0 +// <= 1.2.x will become < 1.3.0 +// > 1.2.x will become >= 1.3.0 +// < 1.2.x will become < 1.2.0 +// != 1.2.x will become < 1.2.0 >= 1.3.0 +// +// * when dealing with minor wildcards: +// >= 1.x will become >= 1.0.0 +// <= 1.x will become < 2.0.0 +// > 1.x will become >= 2.0.0 +// < 1.0 will become < 1.0.0 +// != 1.x will become < 1.0.0 >= 2.0.0 +// +// * when dealing with wildcards without +// version operator: +// 1.2.x will become >= 1.2.0 < 1.3.0 +// 1.x will become >= 1.0.0 < 2.0.0 +func expandWildcardVersion(parts [][]string) ([][]string, error) { + var expandedParts [][]string + for _, p := range parts { + var newParts []string + for _, ap := range p { + if strings.Contains(ap, "x") { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + + versionWildcardType := getWildcardType(vStr) + flatVersion := createVersionFromWildcard(vStr) + + var resultOperator string + var shouldIncrementVersion bool + switch opStr { + case ">": + resultOperator = ">=" + shouldIncrementVersion = true + case ">=": + resultOperator = ">=" + case "<": + resultOperator = "<" + case "<=": + resultOperator = "<" + shouldIncrementVersion = true + case "", "=", "==": + newParts = append(newParts, ">="+flatVersion) + resultOperator = "<" + shouldIncrementVersion = true + case "!=", "!": + newParts = append(newParts, "<"+flatVersion) + resultOperator = ">=" + shouldIncrementVersion = true + } + + var resultVersion string + if shouldIncrementVersion { + switch versionWildcardType { + case patchWildcard: + resultVersion, _ = incrementMinorVersion(flatVersion) + case minorWildcard: + resultVersion, _ = incrementMajorVersion(flatVersion) + } + } else { + resultVersion = flatVersion + } + + ap = resultOperator + resultVersion + } + newParts = append(newParts, ap) + } + expandedParts = append(expandedParts, newParts) + } + + return expandedParts, nil +} + +func parseComparator(s string) comparator { + switch s { + case "==": + fallthrough + case "": + fallthrough + case "=": + return compEQ + case ">": + return compGT + case ">=": + return compGE + case "<": + return compLT + case "<=": + return compLE + case "!": + fallthrough + case "!=": + return compNE + } + + return nil +} + +// MustParseRange is like ParseRange but panics if the range cannot be parsed. +func MustParseRange(s string) Range { + r, err := ParseRange(s) + if err != nil { + panic(`semver: ParseRange(` + s + `): ` + err.Error()) + } + return r +} diff --git a/vendor/github.com/blang/semver/semver.go b/vendor/github.com/blang/semver/semver.go new file mode 100644 index 00000000..4165bc79 --- /dev/null +++ b/vendor/github.com/blang/semver/semver.go @@ -0,0 +1,455 @@ +package semver + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + numbers string = "0123456789" + alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + alphanum = alphas + numbers +) + +// SpecVersion is the latest fully supported spec version of semver +var SpecVersion = Version{ + Major: 2, + Minor: 0, + Patch: 0, +} + +// Version represents a semver compatible version +type Version struct { + Major uint64 + Minor uint64 + Patch uint64 + Pre []PRVersion + Build []string //No Precedence +} + +// Version to string +func (v Version) String() string { + b := make([]byte, 0, 5) + b = strconv.AppendUint(b, v.Major, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Minor, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Patch, 10) + + if len(v.Pre) > 0 { + b = append(b, '-') + b = append(b, v.Pre[0].String()...) + + for _, pre := range v.Pre[1:] { + b = append(b, '.') + b = append(b, pre.String()...) + } + } + + if len(v.Build) > 0 { + b = append(b, '+') + b = append(b, v.Build[0]...) + + for _, build := range v.Build[1:] { + b = append(b, '.') + b = append(b, build...) + } + } + + return string(b) +} + +// Equals checks if v is equal to o. +func (v Version) Equals(o Version) bool { + return (v.Compare(o) == 0) +} + +// EQ checks if v is equal to o. +func (v Version) EQ(o Version) bool { + return (v.Compare(o) == 0) +} + +// NE checks if v is not equal to o. +func (v Version) NE(o Version) bool { + return (v.Compare(o) != 0) +} + +// GT checks if v is greater than o. +func (v Version) GT(o Version) bool { + return (v.Compare(o) == 1) +} + +// GTE checks if v is greater than or equal to o. +func (v Version) GTE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// GE checks if v is greater than or equal to o. +func (v Version) GE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// LT checks if v is less than o. +func (v Version) LT(o Version) bool { + return (v.Compare(o) == -1) +} + +// LTE checks if v is less than or equal to o. +func (v Version) LTE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// LE checks if v is less than or equal to o. +func (v Version) LE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// Compare compares Versions v to o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v Version) Compare(o Version) int { + if v.Major != o.Major { + if v.Major > o.Major { + return 1 + } + return -1 + } + if v.Minor != o.Minor { + if v.Minor > o.Minor { + return 1 + } + return -1 + } + if v.Patch != o.Patch { + if v.Patch > o.Patch { + return 1 + } + return -1 + } + + // Quick comparison if a version has no prerelease versions + if len(v.Pre) == 0 && len(o.Pre) == 0 { + return 0 + } else if len(v.Pre) == 0 && len(o.Pre) > 0 { + return 1 + } else if len(v.Pre) > 0 && len(o.Pre) == 0 { + return -1 + } + + i := 0 + for ; i < len(v.Pre) && i < len(o.Pre); i++ { + if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 { + continue + } else if comp == 1 { + return 1 + } else { + return -1 + } + } + + // If all pr versions are the equal but one has further prversion, this one greater + if i == len(v.Pre) && i == len(o.Pre) { + return 0 + } else if i == len(v.Pre) && i < len(o.Pre) { + return -1 + } else { + return 1 + } + +} + +// IncrementPatch increments the patch version +func (v *Version) IncrementPatch() error { + if v.Major == 0 { + return fmt.Errorf("Patch version can not be incremented for %q", v.String()) + } + v.Patch += 1 + return nil +} + +// IncrementMinor increments the minor version +func (v *Version) IncrementMinor() error { + if v.Major == 0 { + return fmt.Errorf("Minor version can not be incremented for %q", v.String()) + } + v.Minor += 1 + v.Patch = 0 + return nil +} + +// IncrementMajor increments the major version +func (v *Version) IncrementMajor() error { + if v.Major == 0 { + return fmt.Errorf("Major version can not be incremented for %q", v.String()) + } + v.Major += 1 + v.Minor = 0 + v.Patch = 0 + return nil +} + +// Validate validates v and returns error in case +func (v Version) Validate() error { + // Major, Minor, Patch already validated using uint64 + + for _, pre := range v.Pre { + if !pre.IsNum { //Numeric prerelease versions already uint64 + if len(pre.VersionStr) == 0 { + return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr) + } + if !containsOnly(pre.VersionStr, alphanum) { + return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr) + } + } + } + + for _, build := range v.Build { + if len(build) == 0 { + return fmt.Errorf("Build meta data can not be empty %q", build) + } + if !containsOnly(build, alphanum) { + return fmt.Errorf("Invalid character(s) found in build meta data %q", build) + } + } + + return nil +} + +// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error +func New(s string) (vp *Version, err error) { + v, err := Parse(s) + vp = &v + return +} + +// Make is an alias for Parse, parses version string and returns a validated Version or error +func Make(s string) (Version, error) { + return Parse(s) +} + +// ParseTolerant allows for certain version specifications that do not strictly adhere to semver +// specs to be parsed by this library. It does so by normalizing versions before passing them to +// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions +// with only major and minor components specified, and removes leading 0s. +func ParseTolerant(s string) (Version, error) { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, "v") + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + // Remove leading zeros. + for i, p := range parts { + if len(p) > 1 { + parts[i] = strings.TrimPrefix(p, "0") + } + } + // Fill up shortened versions. + if len(parts) < 3 { + if strings.ContainsAny(parts[len(parts)-1], "+-") { + return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data") + } + for len(parts) < 3 { + parts = append(parts, "0") + } + } + s = strings.Join(parts, ".") + + return Parse(s) +} + +// Parse parses version string and returns a validated Version or error +func Parse(s string) (Version, error) { + if len(s) == 0 { + return Version{}, errors.New("Version string empty") + } + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + if len(parts) != 3 { + return Version{}, errors.New("No Major.Minor.Patch elements found") + } + + // Major + if !containsOnly(parts[0], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0]) + } + if hasLeadingZeroes(parts[0]) { + return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0]) + } + major, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return Version{}, err + } + + // Minor + if !containsOnly(parts[1], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1]) + } + if hasLeadingZeroes(parts[1]) { + return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1]) + } + minor, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return Version{}, err + } + + v := Version{} + v.Major = major + v.Minor = minor + + var build, prerelease []string + patchStr := parts[2] + + if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 { + build = strings.Split(patchStr[buildIndex+1:], ".") + patchStr = patchStr[:buildIndex] + } + + if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 { + prerelease = strings.Split(patchStr[preIndex+1:], ".") + patchStr = patchStr[:preIndex] + } + + if !containsOnly(patchStr, numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr) + } + if hasLeadingZeroes(patchStr) { + return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr) + } + patch, err := strconv.ParseUint(patchStr, 10, 64) + if err != nil { + return Version{}, err + } + + v.Patch = patch + + // Prerelease + for _, prstr := range prerelease { + parsedPR, err := NewPRVersion(prstr) + if err != nil { + return Version{}, err + } + v.Pre = append(v.Pre, parsedPR) + } + + // Build meta data + for _, str := range build { + if len(str) == 0 { + return Version{}, errors.New("Build meta data is empty") + } + if !containsOnly(str, alphanum) { + return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str) + } + v.Build = append(v.Build, str) + } + + return v, nil +} + +// MustParse is like Parse but panics if the version cannot be parsed. +func MustParse(s string) Version { + v, err := Parse(s) + if err != nil { + panic(`semver: Parse(` + s + `): ` + err.Error()) + } + return v +} + +// PRVersion represents a PreRelease Version +type PRVersion struct { + VersionStr string + VersionNum uint64 + IsNum bool +} + +// NewPRVersion creates a new valid prerelease version +func NewPRVersion(s string) (PRVersion, error) { + if len(s) == 0 { + return PRVersion{}, errors.New("Prerelease is empty") + } + v := PRVersion{} + if containsOnly(s, numbers) { + if hasLeadingZeroes(s) { + return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s) + } + num, err := strconv.ParseUint(s, 10, 64) + + // Might never be hit, but just in case + if err != nil { + return PRVersion{}, err + } + v.VersionNum = num + v.IsNum = true + } else if containsOnly(s, alphanum) { + v.VersionStr = s + v.IsNum = false + } else { + return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s) + } + return v, nil +} + +// IsNumeric checks if prerelease-version is numeric +func (v PRVersion) IsNumeric() bool { + return v.IsNum +} + +// Compare compares two PreRelease Versions v and o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v PRVersion) Compare(o PRVersion) int { + if v.IsNum && !o.IsNum { + return -1 + } else if !v.IsNum && o.IsNum { + return 1 + } else if v.IsNum && o.IsNum { + if v.VersionNum == o.VersionNum { + return 0 + } else if v.VersionNum > o.VersionNum { + return 1 + } else { + return -1 + } + } else { // both are Alphas + if v.VersionStr == o.VersionStr { + return 0 + } else if v.VersionStr > o.VersionStr { + return 1 + } else { + return -1 + } + } +} + +// PreRelease version to string +func (v PRVersion) String() string { + if v.IsNum { + return strconv.FormatUint(v.VersionNum, 10) + } + return v.VersionStr +} + +func containsOnly(s string, set string) bool { + return strings.IndexFunc(s, func(r rune) bool { + return !strings.ContainsRune(set, r) + }) == -1 +} + +func hasLeadingZeroes(s string) bool { + return len(s) > 1 && s[0] == '0' +} + +// NewBuildVersion creates a new valid build version +func NewBuildVersion(s string) (string, error) { + if len(s) == 0 { + return "", errors.New("Buildversion is empty") + } + if !containsOnly(s, alphanum) { + return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s) + } + return s, nil +} diff --git a/vendor/github.com/blang/semver/sort.go b/vendor/github.com/blang/semver/sort.go new file mode 100644 index 00000000..e18f8808 --- /dev/null +++ b/vendor/github.com/blang/semver/sort.go @@ -0,0 +1,28 @@ +package semver + +import ( + "sort" +) + +// Versions represents multiple versions. +type Versions []Version + +// Len returns length of version collection +func (s Versions) Len() int { + return len(s) +} + +// Swap swaps two versions inside the collection by its indices +func (s Versions) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less checks if version at index i is less than version at index j +func (s Versions) Less(i, j int) bool { + return s[i].LT(s[j]) +} + +// Sort sorts a slice of versions +func Sort(versions []Version) { + sort.Sort(Versions(versions)) +} diff --git a/vendor/github.com/blang/semver/sql.go b/vendor/github.com/blang/semver/sql.go new file mode 100644 index 00000000..db958134 --- /dev/null +++ b/vendor/github.com/blang/semver/sql.go @@ -0,0 +1,30 @@ +package semver + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements the database/sql.Scanner interface. +func (v *Version) Scan(src interface{}) (err error) { + var str string + switch src := src.(type) { + case string: + str = src + case []byte: + str = string(src) + default: + return fmt.Errorf("version.Scan: cannot convert %T to string", src) + } + + if t, err := Parse(str); err == nil { + *v = t + } + + return +} + +// Value implements the database/sql/driver.Valuer interface. +func (v Version) Value() (driver.Value, error) { + return v.String(), nil +} diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml deleted file mode 100644 index d003ad92..00000000 --- a/vendor/github.com/gorilla/mux/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: go - - -matrix: - include: - - go: 1.7.x - - go: 1.8.x - - go: 1.9.x - - go: 1.10.x - - go: 1.11.x - - go: 1.x - env: LATEST=true - - go: tip - allow_failures: - - go: tip - -install: - - # Skip - -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d .) - - if [[ "$LATEST" = true ]]; then go vet .; fi - - go test -v -race ./... diff --git a/vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md b/vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md deleted file mode 100644 index 232be82e..00000000 --- a/vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ -**What version of Go are you running?** (Paste the output of `go version`) - - -**What version of gorilla/mux are you at?** (Paste the output of `git rev-parse HEAD` inside `$GOPATH/src/github.com/gorilla/mux`) - - -**Describe your problem** (and what you have tried so far) - - -**Paste a minimal, runnable, reproduction of your issue below** (use backticks to format it) - diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md index c661599a..92e422ee 100644 --- a/vendor/github.com/gorilla/mux/README.md +++ b/vendor/github.com/gorilla/mux/README.md @@ -2,6 +2,7 @@ [![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) [![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) +[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) ![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png) @@ -29,6 +30,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv * [Walking Routes](#walking-routes) * [Graceful Shutdown](#graceful-shutdown) * [Middleware](#middleware) +* [Handling CORS Requests](#handling-cors-requests) * [Testing Handlers](#testing-handlers) * [Full Example](#full-example) @@ -491,6 +493,73 @@ r.Use(amw.Middleware) Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it. +### Handling CORS Requests + +[CORSMethodMiddleware](https://godoc.org/github.com/gorilla/mux#CORSMethodMiddleware) intends to make it easier to strictly set the `Access-Control-Allow-Methods` response header. + +* You will still need to use your own CORS handler to set the other CORS headers such as `Access-Control-Allow-Origin` +* The middleware will set the `Access-Control-Allow-Methods` header to all the method matchers (e.g. `r.Methods(http.MethodGet, http.MethodPut, http.MethodOptions)` -> `Access-Control-Allow-Methods: GET,PUT,OPTIONS`) on a route +* If you do not specify any methods, then: +> _Important_: there must be an `OPTIONS` method matcher for the middleware to set the headers. + +Here is an example of using `CORSMethodMiddleware` along with a custom `OPTIONS` handler to set all the required CORS headers: + +```go +package main + +import ( + "net/http" + "github.com/gorilla/mux" +) + +func main() { + r := mux.NewRouter() + + // IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers + r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions) + r.Use(mux.CORSMethodMiddleware(r)) + + http.ListenAndServe(":8080", r) +} + +func fooHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + if r.Method == http.MethodOptions { + return + } + + w.Write([]byte("foo")) +} +``` + +And an request to `/foo` using something like: + +```bash +curl localhost:8080/foo -v +``` + +Would look like: + +```bash +* Trying ::1... +* TCP_NODELAY set +* Connected to localhost (::1) port 8080 (#0) +> GET /foo HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.59.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS +< Access-Control-Allow-Origin: * +< Date: Fri, 28 Jun 2019 20:13:30 GMT +< Content-Length: 3 +< Content-Type: text/plain; charset=utf-8 +< +* Connection #0 to host localhost left intact +foo +``` + ### Testing Handlers Testing handlers in a Go web application is straightforward, and _mux_ doesn't complicate this any further. Given two files: `endpoints.go` and `endpoints_test.go`, here's how we'd test an application using _mux_. diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go index 38957dee..bd5a38b5 100644 --- a/vendor/github.com/gorilla/mux/doc.go +++ b/vendor/github.com/gorilla/mux/doc.go @@ -295,7 +295,7 @@ A more complex authentication middleware, which maps session token to users, cou r := mux.NewRouter() r.HandleFunc("/", handler) - amw := authenticationMiddleware{} + amw := authenticationMiddleware{tokenUsers: make(map[string]string)} amw.Populate() r.Use(amw.Middleware) diff --git a/vendor/github.com/gorilla/mux/middleware.go b/vendor/github.com/gorilla/mux/middleware.go index ceb812ce..cf2b26dc 100644 --- a/vendor/github.com/gorilla/mux/middleware.go +++ b/vendor/github.com/gorilla/mux/middleware.go @@ -32,37 +32,19 @@ func (r *Router) useInterface(mw middleware) { r.middlewares = append(r.middlewares, mw) } -// CORSMethodMiddleware sets the Access-Control-Allow-Methods response header -// on a request, by matching routes based only on paths. It also handles -// OPTIONS requests, by settings Access-Control-Allow-Methods, and then -// returning without calling the next http handler. +// CORSMethodMiddleware automatically sets the Access-Control-Allow-Methods response header +// on requests for routes that have an OPTIONS method matcher to all the method matchers on +// the route. Routes that do not explicitly handle OPTIONS requests will not be processed +// by the middleware. See examples for usage. func CORSMethodMiddleware(r *Router) MiddlewareFunc { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var allMethods []string - - err := r.Walk(func(route *Route, _ *Router, _ []*Route) error { - for _, m := range route.matchers { - if _, ok := m.(*routeRegexp); ok { - if m.Match(req, &RouteMatch{}) { - methods, err := route.GetMethods() - if err != nil { - return err - } - - allMethods = append(allMethods, methods...) - } - break - } - } - return nil - }) - + allMethods, err := getAllMethodsForRoute(r, req) if err == nil { - w.Header().Set("Access-Control-Allow-Methods", strings.Join(append(allMethods, "OPTIONS"), ",")) - - if req.Method == "OPTIONS" { - return + for _, v := range allMethods { + if v == http.MethodOptions { + w.Header().Set("Access-Control-Allow-Methods", strings.Join(allMethods, ",")) + } } } @@ -70,3 +52,28 @@ func CORSMethodMiddleware(r *Router) MiddlewareFunc { }) } } + +// getAllMethodsForRoute returns all the methods from method matchers matching a given +// request. +func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) { + var allMethods []string + + err := r.Walk(func(route *Route, _ *Router, _ []*Route) error { + for _, m := range route.matchers { + if _, ok := m.(*routeRegexp); ok { + if m.Match(req, &RouteMatch{}) { + methods, err := route.GetMethods() + if err != nil { + return err + } + + allMethods = append(allMethods, methods...) + } + break + } + } + return nil + }) + + return allMethods, err +} |