Skip to content

Commit

Permalink
Merge branch 'master' into feat-directive-parent
Browse files Browse the repository at this point in the history
  • Loading branch information
vvakame committed Aug 23, 2018
2 parents e57464f + 3f8a601 commit c7fd841
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Describe your PR and link to any relevant issues.

I have:
- [ ] Added tests covering the bug / feature (see [testing](https://github.com/99designs/gqlgen/blob/master/TESTING.md))
- [ ] Updated any relevant documentation (see [docs](https://github.com/99designs/gqlgen/tree/master/docs/content))
40 changes: 40 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
How to write tests for gqlgen
===

Testing generated code is a little tricky, heres how its currently set up.

### Testing responses from a server

There is a server in `codegen/testserver` that is generated as part
of `go generate ./...`, and tests written against it.

There are also a bunch of tests in against the examples, feel free to take examples from there.


### Testing the errors generated by the binary

These tests are **really** slow, because they need to run the whole codegen step. Use them very sparingly. If you can, find a way to unit test it instead.

Take a look at `codegen/input_test.go` for an example.

### Testing introspection

Introspection is tested by diffing the output of `graphql get-schema` against an expected output.

Setting up the integration environment is a little tricky:
```bash
cd integration
go generate ./...
go run ./server/server.go
```
in another terminal
```bash
cd integration
npm install
SERVER_URL=http://localhost:8080/query ./node_modules/.bin/graphql get-schema
```

will write the schema to `integration/schema-fetched.graphql`, compare that with `schema-expected.graphql`

CI will run this and fail the build if the two files dont match.

2 changes: 1 addition & 1 deletion client/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (p *Client) Websocket(query string, options ...Option) *Subscription {

if respDataRaw["errors"] != nil {
var errs []*gqlerror.Error
if err = unpack(respDataRaw["errors"], errs); err != nil {
if err = unpack(respDataRaw["errors"], &errs); err != nil {
return err
}
if len(errs) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func GenerateGraphServer(config *codegen.Config) {
os.Exit(1)
}

fmt.Fprintf(os.Stdout, `Exec "go run ./%s" to start GraphQL server`, serverFilename)
fmt.Fprintf(os.Stdout, "Exec \"go run ./%s\" to start GraphQL server\n", serverFilename)
}

func initConfig() *codegen.Config {
Expand Down
11 changes: 11 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"os"

"github.com/99designs/gqlgen/internal/gopath"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -39,6 +40,16 @@ var rootCmd = &cobra.Command{
Long: `This is a library for quickly creating strictly typed graphql servers in golang.
See https://gqlgen.com/ for a getting started guide.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
pwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "unable to determine current workding dir: %s\n", err.Error())
os.Exit(1)
}

if !gopath.Contains(pwd) {
fmt.Fprintf(os.Stderr, "gqlgen must be run from inside your $GOPATH\n")
os.Exit(1)
}
if verbose {
log.SetFlags(0)
} else {
Expand Down
16 changes: 3 additions & 13 deletions codegen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"

"github.com/99designs/gqlgen/internal/gopath"
"github.com/pkg/errors"
"github.com/vektah/gqlparser/ast"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -107,22 +108,11 @@ func (c *PackageConfig) normalize() error {
}

func (c *PackageConfig) ImportPath() string {
dir := filepath.ToSlash(c.Dir())
for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
gopath = filepath.ToSlash(gopath) + "/src/"
if len(gopath) > len(dir) {
continue
}
if strings.EqualFold(gopath, dir[0:len(gopath)]) {
dir = dir[len(gopath):]
break
}
}
return dir
return gopath.MustDir2Import(c.Dir())
}

func (c *PackageConfig) Dir() string {
return filepath.ToSlash(filepath.Dir(c.Filename))
return filepath.Dir(c.Filename)
}

func (c *PackageConfig) Check() error {
Expand Down
26 changes: 0 additions & 26 deletions codegen/config_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package codegen

import (
"go/build"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -58,27 +56,3 @@ func TestLoadDefaultConfig(t *testing.T) {
require.True(t, os.IsNotExist(err))
})
}

func Test_fullPackageName(t *testing.T) {
origBuildContext := build.Default
defer func() { build.Default = origBuildContext }()

t.Run("gopath longer than package name", func(t *testing.T) {
p := PackageConfig{Filename: "/b/src/y/foo/bar/baz.go"}
build.Default.GOPATH = "/a/src/xxxxxxxxxxxxxxxxxxxxxxxx:/b/src/y"
var got string
ok := assert.NotPanics(t, func() { got = p.ImportPath() })
if ok {
assert.Equal(t, "/b/src/y/foo/bar", got)
}
})
t.Run("stop searching on first hit", func(t *testing.T) {
p := PackageConfig{Filename: "/a/src/x/foo/bar/baz.go"}
build.Default.GOPATH = "/a/src/x:/b/src/y"
var got string
ok := assert.NotPanics(t, func() { got = p.ImportPath() })
if ok {
assert.Equal(t, "/a/src/x/foo/bar", got)
}
})
}
10 changes: 3 additions & 7 deletions codegen/import_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import (
"go/build"
"sort"
"strconv"
"strings"

// Import and ignore the ambient imports listed below so dependency managers
// don't prune unused code for us. Both lists should be kept in sync.
_ "github.com/99designs/gqlgen/graphql"
_ "github.com/99designs/gqlgen/graphql/introspection"
"github.com/99designs/gqlgen/internal/gopath"
_ "github.com/vektah/gqlparser"
_ "github.com/vektah/gqlparser/ast"
)
Expand Down Expand Up @@ -55,7 +54,8 @@ func (s *Imports) add(path string) *Import {
return nil
}

if stringHasSuffixFold(s.destDir, path) {
// if we are referencing our own package we dont need an import
if gopath.MustDir2Import(s.destDir) == path {
return nil
}

Expand All @@ -77,10 +77,6 @@ func (s *Imports) add(path string) *Import {
return imp
}

func stringHasSuffixFold(s, suffix string) bool {
return len(s) >= len(suffix) && strings.EqualFold(s[len(s)-len(suffix):], suffix)
}

func (s Imports) finalize() []*Import {
// ensure stable ordering by sorting
sort.Slice(s.imports, func(i, j int) bool {
Expand Down
1 change: 1 addition & 0 deletions docs/content/introduction.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
linkTitle: Introduction
title: Type-safe graphql for golang
description: Generating GraphQL servers in golang with type safety
type: homepage
date: 2018-03-17T13:06:47+11:00
---
Expand Down
13 changes: 13 additions & 0 deletions docs/layouts/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
{{ range .Data.Pages }}
{{ if or .Description (eq .Kind "home") }}
<url>
<loc>{{ .Permalink }}</loc>
{{ if not .Lastmod.IsZero }}<lastmod>{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}
{{ with .Sitemap.ChangeFreq }}<changefreq>{{ . }}</changefreq>{{ end }}
{{ if ge .Sitemap.Priority 0.0 }}<priority>{{ .Sitemap.Priority }}</priority>{{ end }}
</url>
{{ end }}
{{ end }}
</urlset>

36 changes: 27 additions & 9 deletions graphql/introspection/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,34 @@ func (s *Schema) SubscriptionType() *Type {

func (s *Schema) Directives() []Directive {
var res []Directive

for _, d := range s.schema.Directives {
var locs []string
for _, loc := range d.Locations {
locs = append(locs, string(loc))
}
res = append(res, Directive{
Name: d.Name,
Description: d.Description,
Locations: locs,
})
res = append(res, s.directiveFromDef(d))
}

return res
}

func (s *Schema) directiveFromDef(d *ast.DirectiveDefinition) Directive {
var locs []string
for _, loc := range d.Locations {
locs = append(locs, string(loc))
}

var args []InputValue
for _, arg := range d.Arguments {
args = append(args, InputValue{
Name: arg.Name,
Description: arg.Description,
DefaultValue: defaultValue(arg.DefaultValue),
Type: WrapTypeFromType(s.schema, arg.Type),
})
}

return Directive{
Name: d.Name,
Description: d.Description,
Locations: locs,
Args: args,
}
}
35 changes: 34 additions & 1 deletion integration/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions integration/integration-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import {ApolloClient} from "apollo-client";
import fetch from "node-fetch";
import gql from 'graphql-tag';

if (!process.env.SERVER_URL) {
throw "SERVER_URL must be set"
}
var uri = process.env.SERVER_URL || 'http://localhost:8080/query';

const client = new ApolloClient({
link: new HttpLink({uri: process.env.SERVER_URL, fetch: fetch}),
link: new HttpLink({uri, fetch}),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
Expand Down
5 changes: 4 additions & 1 deletion integration/schema-expected.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# source: http://localhost:8080/query
# timestamp: Fri Aug 10 2018 11:50:28 GMT+1000 (AEST)
# timestamp: Thu Aug 23 2018 10:30:37 GMT+1000 (AEST)

"""This directive does magical things"""
directive @magic(kind: Int) on FIELD_DEFINITION

enum DATE_FILTER_OP {
EQ
Expand Down
3 changes: 3 additions & 0 deletions integration/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"This directive does magical things"
directive @magic(kind: Int) on FIELD_DEFINITION

type Element {
child: Element!
error: Boolean!
Expand Down
37 changes: 37 additions & 0 deletions internal/gopath/gopath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package gopath

import (
"fmt"
"go/build"
"path/filepath"
"strings"
)

var NotFound = 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 "", NotFound
}

// 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
}
Loading

0 comments on commit c7fd841

Please sign in to comment.