Add new package bindings option (#169)
Add new package bindings option This commit adds list of packages for which genqlient should automatically generate bindings; it's equivalent to adding all the exported types in the package to `bindings` explicitly. This can be useful when you're both a client and a server of the same schema and want to share types. We don't recommend doing things that way, but the feature isn't too invasive and may be useful for other purposes. Co-authored-by: Ben Kraft <ben@benkraft.org>
This commit is contained in:
@@ -2,10 +2,12 @@ package generate
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -26,6 +28,7 @@ type Config struct {
|
||||
ContextType string `yaml:"context_type"`
|
||||
ClientGetter string `yaml:"client_getter"`
|
||||
Bindings map[string]*TypeBinding `yaml:"bindings"`
|
||||
PackageBindings []*PackageBinding `yaml:"package_bindings"`
|
||||
Optional string `yaml:"optional"`
|
||||
StructReferences bool `yaml:"use_struct_references"`
|
||||
Extensions bool `yaml:"use_extensions"`
|
||||
@@ -52,6 +55,13 @@ type TypeBinding struct {
|
||||
Unmarshaler string `yaml:"unmarshaler"`
|
||||
}
|
||||
|
||||
// A PackageBinding represents a Go package for which genqlient will
|
||||
// automatically generate TypeBindings, and is documented further at:
|
||||
// https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
type PackageBinding struct {
|
||||
Package string `yaml:"package"`
|
||||
}
|
||||
|
||||
// pathJoin is like filepath.Join but 1) it only takes two argsuments,
|
||||
// and b) if the second argument is an absolute path the first argument
|
||||
// is ignored (similar to how python's os.path.join() works).
|
||||
@@ -98,6 +108,42 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error {
|
||||
c.Package = base
|
||||
}
|
||||
|
||||
if len(c.PackageBindings) > 0 {
|
||||
for _, binding := range c.PackageBindings {
|
||||
mode := packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes
|
||||
pkgs, err := packages.Load(&packages.Config{
|
||||
Mode: mode,
|
||||
}, binding.Package)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bindings == nil {
|
||||
c.Bindings = map[string]*TypeBinding{}
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
p := pkg.Types
|
||||
if p == nil || p.Scope() == nil {
|
||||
return errorf(nil, "unable to bind package %s: no types found", binding.Package)
|
||||
}
|
||||
|
||||
for _, typ := range p.Scope().Names() {
|
||||
if token.IsExported(typ) {
|
||||
// Check if type is manual bindings
|
||||
_, exist := c.Bindings[typ]
|
||||
if !exist {
|
||||
pathType := fmt.Sprintf("%s.%s", p.Path(), typ)
|
||||
c.Bindings[typ] = &TypeBinding{
|
||||
Type: pathType,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,11 @@ func TestGenerateWithConfig(t *testing.T) {
|
||||
StructReferences: true,
|
||||
Generated: "generated-structrefs.go",
|
||||
}},
|
||||
{"PackageBindings", "", nil, &Config{
|
||||
PackageBindings: []*PackageBinding{
|
||||
{Package: "github.com/Khan/genqlient/internal/testutil"},
|
||||
},
|
||||
}},
|
||||
{"NoContext", "", nil, &Config{
|
||||
Generated: "generated.go",
|
||||
ContextType: "-",
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
|
||||
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Khan/genqlient/graphql"
|
||||
"github.com/Khan/genqlient/internal/testutil"
|
||||
)
|
||||
|
||||
// SimpleQueryResponse is returned by SimpleQuery on success.
|
||||
type SimpleQueryResponse struct {
|
||||
// user looks up a user by some stuff.
|
||||
//
|
||||
// See UserQueryInput for what stuff is supported.
|
||||
// If query is null, returns the current user.
|
||||
User SimpleQueryUser `json:"user"`
|
||||
}
|
||||
|
||||
// GetUser returns SimpleQueryResponse.User, and is useful for accessing the field via an interface.
|
||||
func (v *SimpleQueryResponse) GetUser() SimpleQueryUser { return v.User }
|
||||
|
||||
// SimpleQueryUser includes the requested fields of the GraphQL type User.
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// A User is a user!
|
||||
type SimpleQueryUser struct {
|
||||
// id is the user's ID.
|
||||
//
|
||||
// It is stable, unique, and opaque, like all good IDs.
|
||||
Id testutil.ID `json:"id"`
|
||||
}
|
||||
|
||||
// GetId returns SimpleQueryUser.Id, and is useful for accessing the field via an interface.
|
||||
func (v *SimpleQueryUser) GetId() testutil.ID { return v.Id }
|
||||
|
||||
func SimpleQuery(
|
||||
ctx context.Context,
|
||||
client graphql.Client,
|
||||
) (*SimpleQueryResponse, error) {
|
||||
req := &graphql.Request{
|
||||
OpName: "SimpleQuery",
|
||||
Query: `
|
||||
query SimpleQuery {
|
||||
user {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
var err error
|
||||
|
||||
var data SimpleQueryResponse
|
||||
resp := &graphql.Response{Data: &data}
|
||||
|
||||
err = client.MakeRequest(
|
||||
ctx,
|
||||
req,
|
||||
resp,
|
||||
)
|
||||
|
||||
return &data, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user