From 251e8514d637b99bb0e52030dc25483f5e74085d Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sat, 26 Jan 2019 02:39:51 +0000 Subject: [PATCH 1/3] Add support for go modules --- .circleci/golang.Dockerfile | 14 +- .circleci/test.sh | 2 +- .golangci.yml | 3 + .gometalinter.json | 14 - Gopkg.lock | 270 ------------------ Gopkg.toml | 29 -- client/websocket.go | 5 +- cmd/root.go | 9 - codegen/build_interface.go | 11 - codegen/build_typedef.go | 14 +- codegen/config/binder.go | 35 +-- codegen/config/config.go | 27 +- codegen/templates/import.go | 29 +- codegen/testserver/generated.go | 5 + codegen/testserver/generated_test.go | 6 +- codegen/testserver/models-gen.go | 47 +++ codegen/testserver/schema.graphql | 5 + codegen_test.go | 56 ---- example/dataloader/addressloader_gen.go | 21 ++ example/dataloader/dataloaders.go | 6 +- example/dataloader/itemsliceloader_gen.go | 21 ++ example/dataloader/ordersliceloader_gen.go | 21 ++ .../scalars/{vendor => }/external/model.go | 0 example/scalars/generated.go | 2 +- example/scalars/model/generated.go | 2 +- example/scalars/model/model.go | 2 +- example/scalars/resolvers.go | 2 +- example/scalars/vendor/readme.md | 1 - generate.go | 20 +- go.mod | 29 ++ go.sum | 68 +++++ handler/graphql_test.go | 2 +- integration/generated.go | 2 +- integration/gqlgen.yml | 2 +- integration/models-go/viewer.go | 2 +- integration/{vendor => }/remote_api/user.go | 0 integration/resolver.go | 6 +- internal/code/imports.go | 45 +++ internal/code/imports_test.go | 32 +++ internal/{gopath => code}/util.go | 9 +- internal/gopath/gopath.go | 37 --- internal/gopath/gopath_test.go | 62 ---- internal/imports/prune.go | 22 +- testdata/generateserver.graphqls | 11 - tools.go | 5 + 45 files changed, 389 insertions(+), 624 deletions(-) create mode 100644 .golangci.yml delete mode 100644 .gometalinter.json delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml delete mode 100644 codegen_test.go rename example/scalars/{vendor => }/external/model.go (100%) delete mode 100644 example/scalars/vendor/readme.md create mode 100644 go.mod create mode 100644 go.sum rename integration/{vendor => }/remote_api/user.go (100%) create mode 100644 internal/code/imports.go create mode 100644 internal/code/imports_test.go rename internal/{gopath => code}/util.go (73%) delete mode 100644 internal/gopath/gopath.go delete mode 100644 internal/gopath/gopath_test.go delete mode 100644 testdata/generateserver.graphqls create mode 100644 tools.go diff --git a/.circleci/golang.Dockerfile b/.circleci/golang.Dockerfile index 7cc98dbcaf6..e5b6443ce97 100644 --- a/.circleci/golang.Dockerfile +++ b/.circleci/golang.Dockerfile @@ -1,12 +1,10 @@ -FROM golang:1.10 +FROM golang:1.11 -RUN curl -L -o /bin/dep https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64 && chmod +x /bin/dep -RUN go get -u github.com/alecthomas/gometalinter github.com/vektah/gorunpkg -RUN gometalinter --install +RUN curl -sL --fail https://github.com/golangci/golangci-lint/releases/download/v1.13/golangci-lint-1.13-linux-amd64.tar.gz | tar zxv --strip-components=1 --dir=/go/bin -WORKDIR /go/src/github.com/99designs/gqlgen +WORKDIR /projects/gqlgen -COPY Gopkg.* /go/src/github.com/99designs/gqlgen/ -RUN dep ensure -v --vendor-only +COPY go.* /projects/gqlgen/ +RUN go mod download -COPY . /go/src/github.com/99designs/gqlgen/ +COPY . /projects/gqlgen diff --git a/.circleci/test.sh b/.circleci/test.sh index 79c98f18f26..37b0e9dc6a9 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -18,4 +18,4 @@ echo "### running testsuite" go test -race ./... echo "### linting" -gometalinter --vendor ./... +golangci-lint run diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000000..2239d630a0f --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,3 @@ +linters-settings: + errcheck: + ignore: fmt:.*,[rR]ead|[wW]rite|[cC]lose,io:Copy diff --git a/.gometalinter.json b/.gometalinter.json deleted file mode 100644 index 07d6ff52339..00000000000 --- a/.gometalinter.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "sort": ["path"], - "Deadline": "5m", - "Linters": { - "errcheck": { - "Command": "errcheck -abspath -ignore '[rR]ead|[wW]rite|Close|[fF]print'", - "Pattern": "PATH:LINE:COL:MESSAGE", - "InstallFrom": "github.com/kisielk/errcheck", - "PartitionStrategy": "packages" - } - }, - "Skip": ["internal/imports/testdata"], - "Disable": ["gas","golint","gocyclo","goconst", "gotype", "maligned", "gosec"] -} diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index ba7c4bd22be..00000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,270 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:99b2e09d42b4d0929e9e6ac496922f5221baa79b60968576d5ce310b44640c3b" - name = "github.com/agnivade/levenshtein" - packages = ["."] - pruneopts = "UT" - revision = "1787a73e302ce294513cfab1982e186f5cf8985f" - -[[projects]] - digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "UT" - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - digest = "1:66ddebb274faa160a4a23394a17ad3c8e15fee9bf5408d13f77d22b61bc7f072" - name = "github.com/go-chi/chi" - packages = ["."] - pruneopts = "UT" - revision = "e83ac2304db3c50cf03d96a2fcd39009d458bc35" - version = "v3.3.2" - -[[projects]] - digest = "1:f3df613325a793ffb3d0ce7644a3bb6f62db45ac744dafe20172fe999c61cdbf" - name = "github.com/gogo/protobuf" - packages = [ - "io", - "proto", - ] - pruneopts = "UT" - revision = "1adfc126b41513cc696b209667c8656ea7aac67c" - version = "v1.0.0" - -[[projects]] - digest = "1:160eabf7a69910fd74f29c692718bc2437c1c1c7d4c9dea9712357752a70e5df" - name = "github.com/gorilla/context" - packages = ["."] - pruneopts = "UT" - revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" - version = "v1.1" - -[[projects]] - digest = "1:88aa9e326e2bd6045a46e00a922954b3e1a9ac5787109f49ac85366df370e1e5" - name = "github.com/gorilla/mux" - packages = ["."] - pruneopts = "UT" - revision = "53c1911da2b537f792e7cafcb446b05ffe33b996" - version = "v1.6.1" - -[[projects]] - digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" - name = "github.com/gorilla/websocket" - packages = ["."] - pruneopts = "UT" - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" - -[[projects]] - branch = "master" - digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db" - name = "github.com/hashicorp/golang-lru" - packages = [ - ".", - "simplelru", - ] - pruneopts = "UT" - revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3" - -[[projects]] - branch = "master" - digest = "1:ab9cfaf00fc5db5fd9d8e5f33da52e62bcc977d1976503dcc2a1492f391bd9ed" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "UT" - revision = "a4e142e9c047c904fa2f1e144d9a84e6133024bc" - -[[projects]] - digest = "1:d07bd28263c09c5d7f6502e8ddac63e027eca0eafcd07632790d958b50360317" - name = "github.com/opentracing/basictracer-go" - packages = [ - ".", - "wire", - ] - pruneopts = "UT" - revision = "1b32af207119a14b1b231d451df3ed04a72efebf" - version = "v1.0.0" - -[[projects]] - digest = "1:450b7623b185031f3a456801155c8320209f75d0d4c4e633c6b1e59d44d6e392" - name = "github.com/opentracing/opentracing-go" - packages = [ - ".", - "ext", - "log", - ] - pruneopts = "UT" - revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" - version = "v1.0.2" - -[[projects]] - digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "UT" - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "UT" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758" - name = "github.com/rs/cors" - packages = ["."] - pruneopts = "UT" - revision = "9a47f48565a795472d43519dd49aac781f3034fb" - version = "v1.6.0" - -[[projects]] - branch = "master" - digest = "1:7ca2584fa7da0520cd2d1136a10194fe5a5b220bdb215074ab6f7b5ad91115f4" - name = "github.com/shurcooL/httpfs" - packages = ["vfsutil"] - pruneopts = "UT" - revision = "809beceb23714880abc4a382a00c05f89d13b1cc" - -[[projects]] - branch = "master" - digest = "1:a3f0c6930421c8a4c02aaf2a00c5e7b808204b15a47b1d8c285c1e9cbda886b5" - name = "github.com/shurcooL/vfsgen" - packages = ["."] - pruneopts = "UT" - revision = "ffb13db8def02f545acc58bd288ec6057c2bbfb9" - -[[projects]] - digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require", - ] - pruneopts = "UT" - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" - -[[projects]] - digest = "1:b24d38b282bacf9791408a080f606370efa3d364e4b5fd9ba0f7b87786d3b679" - name = "github.com/urfave/cli" - packages = ["."] - pruneopts = "UT" - revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" - version = "v1.20.0" - -[[projects]] - branch = "master" - digest = "1:3684f335059f8441260c3e949c0b99de97ea1057ecc493fdbd72ff3c27863277" - name = "github.com/vektah/dataloaden" - packages = ["."] - pruneopts = "UT" - revision = "314ac81052eedc03ac0a79bdc89d05a49a2a5814" - -[[projects]] - digest = "1:ed6a41de3eedd8d4868eea057837860453ebbe08c5e385fd50d4c24e5642ec18" - name = "github.com/vektah/gqlparser" - packages = [ - ".", - "ast", - "gqlerror", - "lexer", - "parser", - "validator", - "validator/rules", - ] - pruneopts = "UT" - revision = "e805d08bb209b1accdea76bd2327811858d81985" - version = "v1.0.0" - -[[projects]] - branch = "master" - digest = "1:76ee51c3f468493aff39dbacc401e8831fbb765104cbf613b89bef01cf4bad70" - name = "golang.org/x/net" - packages = ["context"] - pruneopts = "UT" - revision = "b3c676e531a6dc479fa1b35ac961c13f5e2b4d2e" - -[[projects]] - branch = "master" - digest = "1:3cbc05413b8aac22b1f6d4350ed696b5a83a8515a4136db8f1ec3a0aee3d76e1" - name = "golang.org/x/tools" - packages = [ - "go/ast/astutil", - "go/buildutil", - "go/loader", - "imports", - ] - pruneopts = "UT" - revision = "ce871d178848e3eea1e8795e5cfb74053dde4bb9" - -[[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - -[[projects]] - branch = "master" - digest = "1:741ebea9214cc226789d3003baeca9b169e04b5b336fb1a3b2c16e75bd296bb5" - name = "sourcegraph.com/sourcegraph/appdash" - packages = [ - ".", - "httptrace", - "internal/wire", - "opentracing", - "sqltrace", - "traceapp", - "traceapp/tmpl", - ] - pruneopts = "UT" - revision = "2cc67fd647551af94593ecaaa89fe4e5b2940a3e" - -[[projects]] - branch = "master" - digest = "1:8e0a2957fe342f22d70a543c3fcdf390f7627419c3d82d87ab4fd715a9ef5716" - name = "sourcegraph.com/sourcegraph/appdash-data" - packages = ["."] - pruneopts = "UT" - revision = "73f23eafcf67cad684fba328dd545a116ac273ff" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/go-chi/chi", - "github.com/gorilla/websocket", - "github.com/hashicorp/golang-lru", - "github.com/mitchellh/mapstructure", - "github.com/opentracing/opentracing-go", - "github.com/pkg/errors", - "github.com/rs/cors", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/require", - "github.com/urfave/cli", - "github.com/vektah/dataloaden", - "github.com/vektah/gqlparser", - "github.com/vektah/gqlparser/ast", - "github.com/vektah/gqlparser/gqlerror", - "github.com/vektah/gqlparser/parser", - "github.com/vektah/gqlparser/validator", - "golang.org/x/tools/go/ast/astutil", - "golang.org/x/tools/go/loader", - "golang.org/x/tools/imports", - "gopkg.in/yaml.v2", - "sourcegraph.com/sourcegraph/appdash", - "sourcegraph.com/sourcegraph/appdash/opentracing", - "sourcegraph.com/sourcegraph/appdash/traceapp", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 7876142a7ea..00000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,29 +0,0 @@ -required = ["github.com/vektah/dataloaden"] - -[[constraint]] - branch = "master" - name = "github.com/mitchellh/mapstructure" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "1.2.1" - -[[constraint]] - branch = "master" - name = "golang.org/x/tools" - -[[constraint]] - name = "github.com/vektah/gqlparser" - version = "^1.0.0" - -[prune] - go-tests = true - unused-packages = true - -[[constraint]] - name = "gopkg.in/yaml.v2" - version = "2.2.1" - -[[constraint]] - name = "github.com/rs/cors" - version = "1.6.0" diff --git a/client/websocket.go b/client/websocket.go index d66c872ca30..f7452b4c978 100644 --- a/client/websocket.go +++ b/client/websocket.go @@ -84,7 +84,10 @@ func (p *Client) WebsocketWithPayload(query string, initPayload map[string]inter Close: c.Close, Next: func(response interface{}) error { var op operationMessage - c.ReadJSON(&op) + err := c.ReadJSON(&op) + if err != nil { + return err + } if op.Type != dataMsg { if op.Type == errorMsg { return fmt.Errorf(string(op.Payload)) diff --git a/cmd/root.go b/cmd/root.go index 6e6d6263512..dc2970ac890 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,7 +7,6 @@ import ( "os" "github.com/99designs/gqlgen/graphql" - "github.com/99designs/gqlgen/internal/gopath" "github.com/urfave/cli" // Required since otherwise dep will prune away these unused packages before codegen has a chance to run @@ -23,14 +22,6 @@ func Execute() { app.Flags = genCmd.Flags app.Version = graphql.Version app.Before = func(context *cli.Context) error { - pwd, err := os.Getwd() - if err != nil { - return fmt.Errorf("unable to determine current workding dir: %s", err.Error()) - } - - if !gopath.Contains(pwd) { - return fmt.Errorf("gqlgen must be run from inside your $GOPATH") - } if context.Bool("verbose") { log.SetFlags(0) } else { diff --git a/codegen/build_interface.go b/codegen/build_interface.go index 173f4805cda..1e0578620a4 100644 --- a/codegen/build_interface.go +++ b/codegen/build_interface.go @@ -2,7 +2,6 @@ package codegen import ( "go/types" - "strings" "github.com/vektah/gqlparser/ast" ) @@ -38,13 +37,3 @@ func (b *builder) isValueReceiver(intf *TypeDefinition, implementor *TypeDefinit return types.Implements(implementorType, interfaceType) } - -// take a string in the form github.com/package/blah.TypeReference and split it into package and type -func pkgAndType(name string) (string, string) { - parts := strings.Split(name, ".") - if len(parts) == 1 { - return "", name - } - - return normalizeVendor(strings.Join(parts[:len(parts)-1], ".")), parts[len(parts)-1] -} diff --git a/codegen/build_typedef.go b/codegen/build_typedef.go index 9bfb272b7cd..e167ae416d3 100644 --- a/codegen/build_typedef.go +++ b/codegen/build_typedef.go @@ -4,6 +4,8 @@ import ( "fmt" "go/types" + "github.com/99designs/gqlgen/internal/code" + "github.com/99designs/gqlgen/codegen/templates" "github.com/pkg/errors" "github.com/vektah/gqlparser/ast" @@ -18,12 +20,16 @@ func (b *builder) buildTypeDef(schemaType *ast.Definition) (*TypeDefinition, err if userEntry, ok := b.Config.Models[t.GQLDefinition.Name]; ok && userEntry.Model != "" { // special case for maps if userEntry.Model == "map[string]interface{}" { - t.GoType = types.NewMap(types.Typ[types.String], types.NewInterface(nil, nil).Complete()) + t.GoType = types.NewMap(types.Typ[types.String], types.NewInterfaceType(nil, nil).Complete()) return t, nil } - pkgName, typeName = pkgAndType(userEntry.Model) + pkgName, typeName = code.PkgAndType(userEntry.Model) + if pkgName == "" { + return nil, fmt.Errorf("missing package name for %s", schemaType.Name) + } + } else if t.GQLDefinition.Kind == ast.Scalar { pkgName = "github.com/99designs/gqlgen/graphql" typeName = "String" @@ -39,10 +45,6 @@ func (b *builder) buildTypeDef(schemaType *ast.Definition) (*TypeDefinition, err return t, nil } - if pkgName == "" { - return nil, fmt.Errorf("missing package name for %s", schemaType.Name) - } - // External marshal functions def, err := b.Binder.FindObject(pkgName, typeName) if err != nil { diff --git a/codegen/config/binder.go b/codegen/config/binder.go index 9adb868e8cb..03974ab2552 100644 --- a/codegen/config/binder.go +++ b/codegen/config/binder.go @@ -7,35 +7,24 @@ import ( "strings" "github.com/pkg/errors" - "golang.org/x/tools/go/loader" + "golang.org/x/tools/go/packages" ) // Binder connects graphql types to golang types using static analysis type Binder struct { - program *loader.Program - types TypeMap + pkgs []*packages.Package + types TypeMap } func (c *Config) NewBinder() (*Binder, error) { - conf := loader.Config{ - AllowErrors: true, - TypeChecker: types.Config{ - Error: func(e error) {}, - }, - } - - for _, pkg := range c.Models.ReferencedPackages() { - conf.Import(pkg) - } - - prog, err := conf.Load() + pkgs, err := packages.Load(&packages.Config{Mode: packages.LoadTypes | packages.LoadSyntax}, c.Models.ReferencedPackages()...) if err != nil { - return nil, errors.Wrap(err, "loading program") + return nil, err } return &Binder{ - program: prog, - types: c.Models, + pkgs: pkgs, + types: c.Models, }, nil } @@ -51,9 +40,9 @@ func (b *Binder) FindType(pkgName string, typeName string) (types.Type, error) { return obj.Type(), nil } -func (b *Binder) getPkg(find string) *loader.PackageInfo { - for n, p := range b.program.Imported { - if normalizeVendor(find) == normalizeVendor(n) { +func (b *Binder) getPkg(find string) *packages.Package { + for _, p := range b.pkgs { + if normalizeVendor(find) == normalizeVendor(p.PkgPath) { return p } } @@ -74,9 +63,9 @@ func (b *Binder) FindObject(pkgName string, typeName string) (types.Object, erro return nil, errors.Errorf("required package was not loaded: %s", fullName) } - for astNode, def := range pkg.Defs { + for astNode, def := range pkg.TypesInfo.Defs { // only look at defs in the top scope - if def == nil || def.Parent() == nil || def.Parent() != pkg.Pkg.Scope() { + if def == nil || def.Parent() == nil || def.Parent() != pkg.Types.Scope() { continue } diff --git a/codegen/config/config.go b/codegen/config/config.go index 1c05fcf3186..688763d5843 100644 --- a/codegen/config/config.go +++ b/codegen/config/config.go @@ -2,21 +2,19 @@ package config import ( "fmt" - "go/build" "io/ioutil" "os" "path/filepath" - "regexp" "sort" "strings" "go/types" - "github.com/99designs/gqlgen/internal/gopath" + "github.com/99designs/gqlgen/internal/code" "github.com/pkg/errors" "github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" ) type Config struct { @@ -139,20 +137,14 @@ func (c *PackageConfig) normalize() error { // If Package is not set, first attempt to load the package at the output dir. If that fails // fallback to just the base dir name of the output filename. if c.Package == "" { - cwd, _ := os.Getwd() - pkg, _ := build.Default.Import(c.ImportPath(), cwd, 0) - if pkg.Name != "" { - c.Package = pkg.Name - } else { - c.Package = filepath.Base(c.Dir()) - } + c.Package = code.NameForPackage(c.ImportPath()) } - c.Package = sanitizePackageName(c.Package) + return nil } func (c *PackageConfig) ImportPath() string { - return gopath.MustDir2Import(c.Dir()) + return code.ImportPathForDir(c.Dir()) } func (c *PackageConfig) Dir() string { @@ -225,7 +217,7 @@ func (tm TypeMap) ReferencedPackages() []string { if typ.Model == "map[string]interface{}" { continue } - pkg, _ := gopath.PkgAndType(typ.Model) + pkg, _ := code.PkgAndType(typ.Model) if pkg == "" || inStrSlice(pkgs, pkg) { continue } @@ -346,6 +338,7 @@ func (c *Config) LoadSchema() (*ast.Schema, map[string]string, error) { var sources []*ast.Source for _, filename := range c.SchemaFilename { + filename = filepath.ToSlash(filename) var err error var schemaRaw []byte schemaRaw, err = ioutil.ReadFile(filename) @@ -364,12 +357,6 @@ func (c *Config) LoadSchema() (*ast.Schema, map[string]string, error) { return schema, schemaStrings, nil } -var invalidPackageNameChar = regexp.MustCompile(`[^\w]`) - -func sanitizePackageName(pkg string) string { - return invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_") -} - func abs(path string) string { absPath, err := filepath.Abs(path) if err != nil { diff --git a/codegen/templates/import.go b/codegen/templates/import.go index c83f5672e52..7959e6a1c5d 100644 --- a/codegen/templates/import.go +++ b/codegen/templates/import.go @@ -2,12 +2,10 @@ package templates import ( "fmt" - "go/build" - "strconv" - "go/types" + "strconv" - "github.com/99designs/gqlgen/internal/gopath" + "github.com/99designs/gqlgen/internal/code" ) type Import struct { @@ -46,18 +44,14 @@ func (s *Imports) Reserve(path string, aliases ...string) string { } // if we are referencing our own package we dont need an import - if gopath.MustDir2Import(s.destDir) == path { + if code.ImportPathForDir(s.destDir) == path { return "" } - pkg, err := build.Default.Import(path, s.destDir, 0) - if err != nil { - panic(err) - } - + name := code.NameForPackage(path) var alias string if len(aliases) != 1 { - alias = pkg.Name + alias = name } else { alias = aliases[0] } @@ -71,7 +65,7 @@ func (s *Imports) Reserve(path string, aliases ...string) string { } s.imports = append(s.imports, &Import{ - Name: pkg.Name, + Name: name, Path: path, Alias: alias, }) @@ -84,10 +78,10 @@ func (s *Imports) Lookup(path string) string { return "" } - path = gopath.NormalizeVendor(path) + path = code.NormalizeVendor(path) // if we are referencing our own package we dont need an import - if gopath.MustDir2Import(s.destDir) == path { + if code.ImportPathForDir(s.destDir) == path { return "" } @@ -95,13 +89,8 @@ func (s *Imports) Lookup(path string) string { return existing.Alias } - pkg, err := build.Default.Import(path, s.destDir, 0) - if err != nil { - panic(err) - } - imp := &Import{ - Name: pkg.Name, + Name: code.NameForPackage(path), Path: path, } s.imports = append(s.imports, imp) diff --git a/codegen/testserver/generated.go b/codegen/testserver/generated.go index 7dba7381acf..699427fba21 100644 --- a/codegen/testserver/generated.go +++ b/codegen/testserver/generated.go @@ -5239,6 +5239,11 @@ type EmbeddedPointer { directive @length(min: Int!, max: Int, message: String!) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION directive @range(min: Int = 0, max: Int, message: String) on ARGUMENT_DEFINITION + +enum Status { + OK + ERROR +} `}, ) diff --git a/codegen/testserver/generated_test.go b/codegen/testserver/generated_test.go index dab97398f44..4280c498e87 100644 --- a/codegen/testserver/generated_test.go +++ b/codegen/testserver/generated_test.go @@ -1,4 +1,3 @@ -//go:generate rm -f resolver.go //go:generate go run ../../testdata/gqlgen.go package testserver @@ -191,6 +190,11 @@ func TestGeneratedServer(t *testing.T) { require.Nil(t, err) require.Equal(t, "Ok", *resp.NullableArg) }) + + t.Run("list of enums", func(t *testing.T) { + require.Equal(t, StatusOk, AllStatus[0]) + require.Equal(t, StatusError, AllStatus[1]) + }) } func TestDirectives(t *testing.T) { diff --git a/codegen/testserver/models-gen.go b/codegen/testserver/models-gen.go index 7f4354ccdc9..b358d12a397 100644 --- a/codegen/testserver/models-gen.go +++ b/codegen/testserver/models-gen.go @@ -2,6 +2,12 @@ package testserver +import ( + "fmt" + "io" + "strconv" +) + type InnerDirectives struct { Message string `json:"message"` } @@ -60,3 +66,44 @@ type User struct { ID int `json:"id"` Friends []User `json:"friends"` } + +type Status string + +const ( + StatusOk Status = "OK" + StatusError Status = "ERROR" +) + +var AllStatus = []Status{ + StatusOk, + StatusError, +} + +func (e Status) IsValid() bool { + switch e { + case StatusOk, StatusError: + return true + } + return false +} + +func (e Status) String() string { + return string(e) +} + +func (e *Status) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = Status(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid Status", str) + } + return nil +} + +func (e Status) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} diff --git a/codegen/testserver/schema.graphql b/codegen/testserver/schema.graphql index 812d93b2372..d08a37d54c7 100644 --- a/codegen/testserver/schema.graphql +++ b/codegen/testserver/schema.graphql @@ -167,3 +167,8 @@ type EmbeddedPointer { directive @length(min: Int!, max: Int, message: String!) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION directive @range(min: Int = 0, max: Int, message: String) on ARGUMENT_DEFINITION + +enum Status { + OK + ERROR +} diff --git a/codegen_test.go b/codegen_test.go deleted file mode 100644 index 0d24de856f6..00000000000 --- a/codegen_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package gqlgen - -import ( - "testing" - - "github.com/99designs/gqlgen/codegen/config" - "github.com/stretchr/testify/require" - "golang.org/x/tools/go/loader" -) - -func TestGenerateServer(t *testing.T) { - name := "graphserver" - - cfg := &config.Config{ - SchemaFilename: config.SchemaFilenames{"testdata/generateserver.graphqls"}, - Exec: config.PackageConfig{Filename: "gen/" + name + "/exec.go"}, - Model: config.PackageConfig{Filename: "gen/" + name + "/model.go"}, - Resolver: config.PackageConfig{Filename: "gen/" + name + "/resolver.go", Type: "Resolver"}, - } - serverFilename := "gen/" + name + "/server/server.go" - - require.NoError(t, Generate(cfg)) - require.NoError(t, GenerateServer(serverFilename, cfg)) - - conf := loader.Config{} - conf.CreateFromFilenames("gen/"+name, serverFilename) - - _, err := conf.Load() - require.NoError(t, err) - - t.Run("list of enums", func(t *testing.T) { - conf = loader.Config{} - conf.CreateFromFilenames("gen/"+name, "gen/"+name+"/model.go") - - program, err := conf.Load() - require.NoError(t, err) - - found := false - - for _, c := range program.Created { - for ident := range c.Defs { - if ident.Name == "AllStatus" { - found = true - break - } - } - if found { - break - } - } - - if !found { - t.Fail() - } - }) -} diff --git a/example/dataloader/addressloader_gen.go b/example/dataloader/addressloader_gen.go index b99ad3635d3..90e1d433fc5 100644 --- a/example/dataloader/addressloader_gen.go +++ b/example/dataloader/addressloader_gen.go @@ -7,6 +7,27 @@ import ( "time" ) +// AddressLoaderConfig captures the config to create a new AddressLoader +type AddressLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []int) ([]*Address, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewAddressLoader creates a new AddressLoader given a fetch, wait, and maxBatch +func NewAddressLoader(config AddressLoaderConfig) *AddressLoader { + return &AddressLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + // AddressLoader batches and caches requests type AddressLoader struct { // this method provides the data for the loader diff --git a/example/dataloader/dataloaders.go b/example/dataloader/dataloaders.go index 8e4efe56edd..05944e5d5e0 100644 --- a/example/dataloader/dataloaders.go +++ b/example/dataloader/dataloaders.go @@ -1,6 +1,6 @@ -//go:generate gorunpkg github.com/vektah/dataloaden -keys int github.com/99designs/gqlgen/example/dataloader.Address -//go:generate gorunpkg github.com/vektah/dataloaden -keys int -slice github.com/99designs/gqlgen/example/dataloader.Order -//go:generate gorunpkg github.com/vektah/dataloaden -keys int -slice github.com/99designs/gqlgen/example/dataloader.Item +//go:generate go run github.com/vektah/dataloaden -keys int github.com/99designs/gqlgen/example/dataloader.Address +//go:generate go run github.com/vektah/dataloaden -keys int -slice github.com/99designs/gqlgen/example/dataloader.Order +//go:generate go run github.com/vektah/dataloaden -keys int -slice github.com/99designs/gqlgen/example/dataloader.Item package dataloader diff --git a/example/dataloader/itemsliceloader_gen.go b/example/dataloader/itemsliceloader_gen.go index 55620a50560..5fa10078401 100644 --- a/example/dataloader/itemsliceloader_gen.go +++ b/example/dataloader/itemsliceloader_gen.go @@ -7,6 +7,27 @@ import ( "time" ) +// ItemSliceLoaderConfig captures the config to create a new ItemSliceLoader +type ItemSliceLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []int) ([][]Item, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewItemSliceLoader creates a new ItemSliceLoader given a fetch, wait, and maxBatch +func NewItemSliceLoader(config ItemSliceLoaderConfig) *ItemSliceLoader { + return &ItemSliceLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + // ItemSliceLoader batches and caches requests type ItemSliceLoader struct { // this method provides the data for the loader diff --git a/example/dataloader/ordersliceloader_gen.go b/example/dataloader/ordersliceloader_gen.go index 432559040da..e76e24d31f0 100644 --- a/example/dataloader/ordersliceloader_gen.go +++ b/example/dataloader/ordersliceloader_gen.go @@ -7,6 +7,27 @@ import ( "time" ) +// OrderSliceLoaderConfig captures the config to create a new OrderSliceLoader +type OrderSliceLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []int) ([][]Order, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewOrderSliceLoader creates a new OrderSliceLoader given a fetch, wait, and maxBatch +func NewOrderSliceLoader(config OrderSliceLoaderConfig) *OrderSliceLoader { + return &OrderSliceLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + // OrderSliceLoader batches and caches requests type OrderSliceLoader struct { // this method provides the data for the loader diff --git a/example/scalars/vendor/external/model.go b/example/scalars/external/model.go similarity index 100% rename from example/scalars/vendor/external/model.go rename to example/scalars/external/model.go diff --git a/example/scalars/generated.go b/example/scalars/generated.go index f6c8a2efc16..c2250131ccb 100644 --- a/example/scalars/generated.go +++ b/example/scalars/generated.go @@ -6,11 +6,11 @@ import ( "bytes" "context" "errors" - "external" "strconv" "sync" "time" + "github.com/99designs/gqlgen/example/scalars/external" "github.com/99designs/gqlgen/example/scalars/model" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" diff --git a/example/scalars/model/generated.go b/example/scalars/model/generated.go index a79b765819f..2ee63fc8aba 100644 --- a/example/scalars/model/generated.go +++ b/example/scalars/model/generated.go @@ -3,7 +3,7 @@ package model import ( - "external" + "github.com/99designs/gqlgen/example/scalars/external" ) type Address struct { diff --git a/example/scalars/model/model.go b/example/scalars/model/model.go index 86038629b0e..d95cb8ff393 100644 --- a/example/scalars/model/model.go +++ b/example/scalars/model/model.go @@ -2,13 +2,13 @@ package model import ( "errors" - "external" "fmt" "io" "strconv" "strings" "time" + "github.com/99designs/gqlgen/example/scalars/external" "github.com/99designs/gqlgen/graphql" ) diff --git a/example/scalars/resolvers.go b/example/scalars/resolvers.go index 3a89683fdbd..b2661121f83 100644 --- a/example/scalars/resolvers.go +++ b/example/scalars/resolvers.go @@ -4,10 +4,10 @@ package scalars import ( context "context" - "external" "fmt" time "time" + "github.com/99designs/gqlgen/example/scalars/external" "github.com/99designs/gqlgen/example/scalars/model" ) diff --git a/example/scalars/vendor/readme.md b/example/scalars/vendor/readme.md deleted file mode 100644 index a7ae70d827f..00000000000 --- a/example/scalars/vendor/readme.md +++ /dev/null @@ -1 +0,0 @@ -fake vendor directory for scalar tests. diff --git a/generate.go b/generate.go index c93dcbca634..8576aba0d36 100644 --- a/generate.go +++ b/generate.go @@ -4,12 +4,13 @@ import ( "path/filepath" "syscall" + "golang.org/x/tools/go/packages" + "github.com/99designs/gqlgen/codegen" "github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/plugin" "github.com/99designs/gqlgen/plugin/modelgen" "github.com/pkg/errors" - "golang.org/x/tools/go/loader" ) func Generate(cfg *config.Config, option ...Option) error { @@ -32,7 +33,6 @@ func Generate(cfg *config.Config, option ...Option) error { } } } - // Merge again now that the generated models have been injected into the typemap schema, err := codegen.NewSchema(cfg) if err != nil { @@ -57,19 +57,19 @@ func Generate(cfg *config.Config, option ...Option) error { } func validate(cfg *config.Config) error { - conf := loader.Config{} - - conf.Import(cfg.Exec.ImportPath()) + roots := []string{cfg.Exec.ImportPath()} if cfg.Model.IsDefined() { - conf.Import(cfg.Model.ImportPath()) + roots = append(roots, cfg.Model.ImportPath()) } if cfg.Resolver.IsDefined() { - conf.Import(cfg.Resolver.ImportPath()) + roots = append(roots, cfg.Resolver.ImportPath()) } - - _, err := conf.Load() - return err + _, err := packages.Load(&packages.Config{Mode: packages.LoadTypes | packages.LoadSyntax}, roots...) + if err != nil { + return errors.Wrap(err, "validation failed") + } + return nil } func abs(path string) string { diff --git a/go.mod b/go.mod new file mode 100644 index 00000000000..efea31cea28 --- /dev/null +++ b/go.mod @@ -0,0 +1,29 @@ +module github.com/99designs/gqlgen + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-chi/chi v3.3.2+incompatible + github.com/gogo/protobuf v1.0.0 // indirect + github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f // indirect + github.com/gorilla/mux v1.6.1 // indirect + github.com/gorilla/websocket v1.2.0 + github.com/hashicorp/golang-lru v0.5.0 + github.com/kr/pretty v0.1.0 // indirect + github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 + github.com/opentracing/basictracer-go v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.0.2 + github.com/pkg/errors v0.8.1 + github.com/rs/cors v1.6.0 + github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 // indirect + github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0 // indirect + github.com/stretchr/testify v1.3.0 + github.com/urfave/cli v1.20.0 + github.com/vektah/dataloaden v0.2.0 + github.com/vektah/gqlparser v1.1.0 + golang.org/x/net v0.0.0-20180404174746-b3c676e531a6 // indirect + golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v2 v2.2.2 + sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755 + sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000000..161ab5ad278 --- /dev/null +++ b/go.sum @@ -0,0 +1,68 @@ +github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ= +github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM= +github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk= +github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.1 h1:KOwqsTYZdeuMacU7CxjMNYEKeBvLbxW+psodrbcEa3A= +github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8= +github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0 h1:JJV9CsgM9EC9w2iVkwuz+sMx8yRFe89PJRUrv6hPCIA= +github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/dataloaden v0.2.0 h1:lhynDrG7c8mNLahboCo0Wq82tMjmu5yOUv2ds/tBmss= +github.com/vektah/dataloaden v0.2.0/go.mod h1:vxM6NuRlgiR0M6wbVTJeKp9vQIs81ZMfCYO+4yq/jbE= +github.com/vektah/gqlparser v1.1.0 h1:3668p2gUlO+PiS81x957Rpr3/FPRWG6cxgCXAvTS1hw= +github.com/vektah/gqlparser v1.1.0/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +golang.org/x/net v0.0.0-20180404174746-b3c676e531a6 h1:mge3qS/eMvcfyIAzTMOAy0XUzWG6Lk0N4M8zjuSmdco= +golang.org/x/net v0.0.0-20180404174746-b3c676e531a6/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755 h1:d2maSb13hr/ArmfK3rW+wNUKKfytCol7W1/vDHxMPiE= +sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 h1:e1sMhtVq9AfcEy8AXNb8eSg6gbzfdpYhoNqnPJa+GzI= +sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/handler/graphql_test.go b/handler/graphql_test.go index 8dff6c182ed..377b773f901 100644 --- a/handler/graphql_test.go +++ b/handler/graphql_test.go @@ -112,7 +112,7 @@ func TestHandlerOptions(t *testing.T) { resp := doRequest(h, "OPTIONS", "/graphql?query={me{name}}", ``) assert.Equal(t, http.StatusOK, resp.Code) - assert.Equal(t, "OPTIONS, GET, POST", resp.HeaderMap.Get("Allow")) + assert.Equal(t, "OPTIONS, GET, POST", resp.Header().Get("Allow")) } func TestHandlerHead(t *testing.T) { diff --git a/integration/generated.go b/integration/generated.go index 6eca435adb2..8336654b9d7 100644 --- a/integration/generated.go +++ b/integration/generated.go @@ -6,13 +6,13 @@ import ( "bytes" "context" "errors" - "remote_api" "strconv" "sync" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" "github.com/99designs/gqlgen/integration/models-go" + "github.com/99designs/gqlgen/integration/remote_api" "github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast" ) diff --git a/integration/gqlgen.yml b/integration/gqlgen.yml index dcd0c0122c1..e3ca845f942 100644 --- a/integration/gqlgen.yml +++ b/integration/gqlgen.yml @@ -16,7 +16,7 @@ models: Viewer: model: github.com/99designs/gqlgen/integration/models-go.Viewer User: - model: remote_api.User + model: github.com/99designs/gqlgen/integration/remote_api.User fields: likes: resolver: true diff --git a/integration/models-go/viewer.go b/integration/models-go/viewer.go index 9b0c9811277..a0201a54e3d 100644 --- a/integration/models-go/viewer.go +++ b/integration/models-go/viewer.go @@ -1,6 +1,6 @@ package models -import "remote_api" +import "github.com/99designs/gqlgen/integration/remote_api" type Viewer struct { User *remote_api.User diff --git a/integration/vendor/remote_api/user.go b/integration/remote_api/user.go similarity index 100% rename from integration/vendor/remote_api/user.go rename to integration/remote_api/user.go diff --git a/integration/resolver.go b/integration/resolver.go index 787be65c828..2edb9941f6e 100644 --- a/integration/resolver.go +++ b/integration/resolver.go @@ -5,10 +5,10 @@ package integration import ( "context" "fmt" - "remote_api" "time" - "github.com/99designs/gqlgen/integration/models-go" + models "github.com/99designs/gqlgen/integration/models-go" + "github.com/99designs/gqlgen/integration/remote_api" ) type CustomError struct { @@ -62,7 +62,7 @@ func (r *queryResolver) Error(ctx context.Context, typeArg *models.ErrorType) (b } func (r *queryResolver) Path(ctx context.Context) ([]*models.Element, error) { - return []*models.Element{{1}, {2}, {3}, {4}}, nil + return []*models.Element{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}}, nil } func (r *queryResolver) Date(ctx context.Context, filter models.DateFilter) (bool, error) { diff --git a/internal/code/imports.go b/internal/code/imports.go new file mode 100644 index 00000000000..27343212799 --- /dev/null +++ b/internal/code/imports.go @@ -0,0 +1,45 @@ +package code + +import ( + "path/filepath" + "sync" + + "golang.org/x/tools/go/packages" +) + +var pathForDirCache = sync.Map{} + +// ImportPathFromDir takes an *absolute* path and returns a golang import path for the package, and returns an error if it isn't on the gopath +func ImportPathForDir(dir string) string { + if v, ok := pathForDirCache.Load(dir); ok { + return v.(string) + } + p, _ := packages.Load(&packages.Config{ + Dir: dir, + }, ".") + + if len(p) != 1 { + return "" + } + + pathForDirCache.Store(dir, p[0].PkgPath) + + return p[0].PkgPath +} + +var nameForPackageCache = sync.Map{} + +func NameForPackage(importPath string) string { + if v, ok := nameForPackageCache.Load(importPath); ok { + return v.(string) + } + p, _ := packages.Load(nil, importPath) + + if len(p) != 1 || p[0].Name == "" { + return SanitizePackageName(filepath.Base(importPath)) + } + + nameForPackageCache.Store(importPath, p[0].Name) + + return p[0].Name +} diff --git a/internal/code/imports_test.go b/internal/code/imports_test.go new file mode 100644 index 00000000000..2612ffce5fb --- /dev/null +++ b/internal/code/imports_test.go @@ -0,0 +1,32 @@ +package code + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestImportPathForDir(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + + assert.Equal(t, "github.com/99designs/gqlgen/internal/code", ImportPathForDir(wd)) + assert.Equal(t, "github.com/99designs/gqlgen", ImportPathForDir(filepath.Join(wd, "..", ".."))) + + // doesnt contain go code, but should still give a valid import path + assert.Equal(t, "github.com/99designs/gqlgen/docs", ImportPathForDir(filepath.Join(wd, "..", "..", "docs"))) + + // directory does not exist + assert.Equal(t, "", ImportPathForDir(filepath.Join(wd, "..", "..", "dos"))) +} + +func TestNameForPackage(t *testing.T) { + assert.Equal(t, "gqlgen", NameForPackage("github.com/99designs/gqlgen")) + + // does not contain go code, should still give a valid name + assert.Equal(t, "docs", NameForPackage("github.com/99designs/gqlgen/docs")) + assert.Equal(t, "github_com", NameForPackage("github.com")) +} diff --git a/internal/gopath/util.go b/internal/code/util.go similarity index 73% rename from internal/gopath/util.go rename to internal/code/util.go index 5d0babe3f12..6e06e4cc187 100644 --- a/internal/gopath/util.go +++ b/internal/code/util.go @@ -1,6 +1,7 @@ -package gopath +package code import ( + "path/filepath" "regexp" "strings" ) @@ -23,3 +24,9 @@ func NormalizeVendor(pkg string) string { parts := strings.Split(pkg, "/vendor/") return modifiers + parts[len(parts)-1] } + +var invalidPackageNameChar = regexp.MustCompile(`[^\w]`) + +func SanitizePackageName(pkg string) string { + return invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_") +} diff --git a/internal/gopath/gopath.go b/internal/gopath/gopath.go deleted file mode 100644 index 01cd5ba955e..00000000000 --- a/internal/gopath/gopath.go +++ /dev/null @@ -1,37 +0,0 @@ -package gopath - -import ( - "fmt" - "go/build" - "path/filepath" - "strings" -) - -var ErrNotFound = fmt.Errorf("not on GOPATH") - -// Contains returns true if the given directory is in the GOPATH -func Contains(dir string) bool { - _, err := Dir2Import(dir) - return err == nil -} - -// Dir2Import takes an *absolute* path and returns a golang import path for the package, and returns an error if it isn't on the gopath -func Dir2Import(dir string) (string, error) { - dir = filepath.ToSlash(dir) - for _, gopath := range filepath.SplitList(build.Default.GOPATH) { - gopath = filepath.ToSlash(filepath.Join(gopath, "src")) - if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) { - return dir[len(gopath)+1:], nil - } - } - return "", ErrNotFound -} - -// MustDir2Import takes an *absolute* path and returns a golang import path for the package, and panics if it isn't on the gopath -func MustDir2Import(dir string) string { - pkg, err := Dir2Import(dir) - if err != nil { - panic(err) - } - return pkg -} diff --git a/internal/gopath/gopath_test.go b/internal/gopath/gopath_test.go deleted file mode 100644 index ae48ab4fe95..00000000000 --- a/internal/gopath/gopath_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package gopath - -import ( - "go/build" - "runtime" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestContains(t *testing.T) { - origBuildContext := build.Default - defer func() { build.Default = origBuildContext }() - - if runtime.GOOS == "windows" { - build.Default.GOPATH = `C:\go;C:\Users\user\go` - - assert.True(t, Contains(`C:\go\src\github.com\vektah\gqlgen`)) - assert.True(t, Contains(`C:\go\src\fpp`)) - assert.True(t, Contains(`C:/go/src/github.com/vektah/gqlgen`)) - assert.True(t, Contains(`C:\Users\user\go\src\foo`)) - assert.False(t, Contains(`C:\tmp`)) - assert.False(t, Contains(`C:\Users\user`)) - assert.False(t, Contains(`C:\Users\another\go`)) - } else { - build.Default.GOPATH = "/go:/home/user/go" - - assert.True(t, Contains("/go/src/github.com/vektah/gqlgen")) - assert.True(t, Contains("/go/src/foo")) - assert.True(t, Contains("/home/user/go/src/foo")) - assert.False(t, Contains("/tmp")) - assert.False(t, Contains("/home/user")) - assert.False(t, Contains("/home/another/go")) - } -} - -func TestDir2Package(t *testing.T) { - origBuildContext := build.Default - defer func() { build.Default = origBuildContext }() - - if runtime.GOOS == "windows" { - build.Default.GOPATH = "C:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;C:/a/y;C:/b/" - - assert.Equal(t, "foo/bar", MustDir2Import("C:/a/y/src/foo/bar")) - assert.Equal(t, "foo/bar", MustDir2Import(`C:\a\y\src\foo\bar`)) - assert.Equal(t, "foo/bar", MustDir2Import("C:/b/src/foo/bar")) - assert.Equal(t, "foo/bar", MustDir2Import(`C:\b\src\foo\bar`)) - - assert.PanicsWithValue(t, ErrNotFound, func() { - MustDir2Import("C:/tmp/foo") - }) - } else { - build.Default.GOPATH = "/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:/a/y:/b/" - - assert.Equal(t, "foo/bar", MustDir2Import("/a/y/src/foo/bar")) - assert.Equal(t, "foo/bar", MustDir2Import("/b/src/foo/bar")) - - assert.PanicsWithValue(t, ErrNotFound, func() { - MustDir2Import("/tmp/foo") - }) - } -} diff --git a/internal/imports/prune.go b/internal/imports/prune.go index d2469e83703..d678870efa7 100644 --- a/internal/imports/prune.go +++ b/internal/imports/prune.go @@ -5,16 +5,15 @@ package imports import ( "bytes" "go/ast" - "go/build" "go/parser" "go/printer" "go/token" - "path/filepath" "strings" - "golang.org/x/tools/imports" + "github.com/99designs/gqlgen/internal/code" "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/imports" ) type visitFn func(node ast.Node) @@ -54,12 +53,6 @@ func getUnusedImports(file ast.Node, filename string) (map[string]string, error) imported := map[string]*ast.ImportSpec{} used := map[string]bool{} - abs, err := filepath.Abs(filename) - if err != nil { - return nil, err - } - srcDir := filepath.Dir(abs) - ast.Walk(visitFn(func(node ast.Node) { if node == nil { return @@ -75,7 +68,7 @@ func getUnusedImports(file ast.Node, filename string) (map[string]string, error) break } - local := importPathToName(ipath, srcDir) + local := code.NameForPackage(ipath) imported[local] = v case *ast.SelectorExpr: @@ -108,12 +101,3 @@ func getUnusedImports(file ast.Node, filename string) (map[string]string, error) return unusedImport, nil } - -func importPathToName(importPath, srcDir string) (packageName string) { - pkg, err := build.Default.Import(importPath, srcDir, 0) - if err != nil { - return "" - } - - return pkg.Name -} diff --git a/testdata/generateserver.graphqls b/testdata/generateserver.graphqls deleted file mode 100644 index bcb0a83a375..00000000000 --- a/testdata/generateserver.graphqls +++ /dev/null @@ -1,11 +0,0 @@ -type Query { - user: User -} -type User { - id: Int - fist_name: String -} -enum Status { - OK - ERROR -} diff --git a/tools.go b/tools.go new file mode 100644 index 00000000000..912fc0d6354 --- /dev/null +++ b/tools.go @@ -0,0 +1,5 @@ +// +build tools + +package main + +import _ "github.com/vektah/dataloaden" From 9e02a977daaba56f7c189a54bcc13f2d53e0d2f2 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sat, 26 Jan 2019 07:17:30 +0000 Subject: [PATCH 2/3] fix integration test --- .circleci/config.yml | 2 +- .circleci/golang.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ef4a8afd24b..26e594fb29d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ jobs: gqlgen/golang go run ./integration/server/server.go \ ) - sleep 2 + sleep 20 docker run \ -e SERVER_URL=http://integration_server:1234/query \ diff --git a/.circleci/golang.Dockerfile b/.circleci/golang.Dockerfile index e5b6443ce97..a0ceb286bf9 100644 --- a/.circleci/golang.Dockerfile +++ b/.circleci/golang.Dockerfile @@ -7,4 +7,4 @@ WORKDIR /projects/gqlgen COPY go.* /projects/gqlgen/ RUN go mod download -COPY . /projects/gqlgen +COPY . /projects/gqlgen/ From 48eb6c521259c1b6f80c3849318d7878f1223bf6 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sat, 26 Jan 2019 07:26:24 +0000 Subject: [PATCH 3/3] Update appveyor --- appveyor.yml | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 142446a2534..db475111488 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,18 +3,15 @@ version: "{build}" # Source Config skip_branch_with_pr: true -clone_folder: c:\gopath\src\github.com\99designs\gqlgen +clone_folder: c:\projects\gqlgen # Build host environment: GOPATH: c:\gopath - GOVERSION: 1.10 + GOVERSION: 1.11.5 PATH: '%PATH%;c:\gopath\bin' -branches: - only: ["master", "next"] - init: - git config --global core.autocrlf input @@ -31,28 +28,5 @@ build: false deploy: false test_script: - - go get -u github.com/vektah/gorunpkg github.com/golang/dep/cmd/dep - - dep ensure -vendor-only - go generate ./... - go test -timeout 20m ./... - - - echo "testing init" - - rd /s /q vendor - - go get ./... - - go install - - cd c:\gopath\src\github.com\99designs\ - - mkdir init-project - - cd init-project - - ps: | - Set-Content -Value @" - // +build ignore - - package main - - import "github.com/99designs/gqlgen/cmd" - - func main() { - cmd.Execute() - } - "@ -Path .\gqlgen.go - - go run gqlgen.go init