stash/vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go
WithoutPants 7a45943e8e
Stash box client interface (#751)
* Add gql client generation files
* Update dependencies
* Add stash-box client generation to the makefile
* Move scraped scene object matchers to models
* Add stash-box to scrape with dropdown
* Add scrape scene from fingerprint in UI
2020-09-17 19:57:18 +10:00

207 lines
5.5 KiB
Go

package resolvergen
import (
"os"
"path/filepath"
"strings"
"github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/internal/rewrite"
"github.com/99designs/gqlgen/plugin"
"github.com/pkg/errors"
)
func New() plugin.Plugin {
return &Plugin{}
}
type Plugin struct{}
var _ plugin.CodeGenerator = &Plugin{}
func (m *Plugin) Name() string {
return "resolvergen"
}
func (m *Plugin) GenerateCode(data *codegen.Data) error {
if !data.Config.Resolver.IsDefined() {
return nil
}
switch data.Config.Resolver.Layout {
case config.LayoutSingleFile:
return m.generateSingleFile(data)
case config.LayoutFollowSchema:
return m.generatePerSchema(data)
}
return nil
}
func (m *Plugin) generateSingleFile(data *codegen.Data) error {
file := File{}
if _, err := os.Stat(data.Config.Resolver.Filename); err == nil {
// file already exists and we dont support updating resolvers with layout = single so just return
return nil
}
for _, o := range data.Objects {
if o.HasResolvers() {
file.Objects = append(file.Objects, o)
}
for _, f := range o.Fields {
if !f.IsResolver {
continue
}
resolver := Resolver{o, f, `panic("not implemented")`}
file.Resolvers = append(file.Resolvers, &resolver)
}
}
resolverBuild := &ResolverBuild{
File: &file,
PackageName: data.Config.Resolver.Package,
ResolverType: data.Config.Resolver.Type,
HasRoot: true,
}
return templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package,
FileNotice: `// THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.`,
Filename: data.Config.Resolver.Filename,
Data: resolverBuild,
Packages: data.Config.Packages,
})
}
func (m *Plugin) generatePerSchema(data *codegen.Data) error {
rewriter, err := rewrite.New(data.Config.Resolver.Dir())
if err != nil {
return err
}
files := map[string]*File{}
for _, o := range data.Objects {
if o.HasResolvers() {
fn := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
if files[fn] == nil {
files[fn] = &File{}
}
rewriter.MarkStructCopied(templates.LcFirst(o.Name) + templates.UcFirst(data.Config.Resolver.Type))
rewriter.GetMethodBody(data.Config.Resolver.Type, o.Name)
files[fn].Objects = append(files[fn].Objects, o)
}
for _, f := range o.Fields {
if !f.IsResolver {
continue
}
structName := templates.LcFirst(o.Name) + templates.UcFirst(data.Config.Resolver.Type)
implementation := strings.TrimSpace(rewriter.GetMethodBody(structName, f.GoFieldName))
if implementation == "" {
implementation = `panic(fmt.Errorf("not implemented"))`
}
resolver := Resolver{o, f, implementation}
fn := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
if files[fn] == nil {
files[fn] = &File{}
}
files[fn].Resolvers = append(files[fn].Resolvers, &resolver)
}
}
for filename, file := range files {
file.imports = rewriter.ExistingImports(filename)
file.RemainingSource = rewriter.RemainingSource(filename)
}
for filename, file := range files {
resolverBuild := &ResolverBuild{
File: file,
PackageName: data.Config.Resolver.Package,
ResolverType: data.Config.Resolver.Type,
}
err := templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package,
FileNotice: `
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.`,
Filename: filename,
Data: resolverBuild,
Packages: data.Config.Packages,
})
if err != nil {
return err
}
}
if _, err := os.Stat(data.Config.Resolver.Filename); os.IsNotExist(errors.Cause(err)) {
err := templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package,
FileNotice: `
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.`,
Template: `type {{.}} struct {}`,
Filename: data.Config.Resolver.Filename,
Data: data.Config.Resolver.Type,
Packages: data.Config.Packages,
})
if err != nil {
return err
}
}
return nil
}
type ResolverBuild struct {
*File
HasRoot bool
PackageName string
ResolverType string
}
type File struct {
// These are separated because the type definition of the resolver object may live in a different file from the
//resolver method implementations, for example when extending a type in a different graphql schema file
Objects []*codegen.Object
Resolvers []*Resolver
imports []rewrite.Import
RemainingSource string
}
func (f *File) Imports() string {
for _, imp := range f.imports {
if imp.Alias == "" {
_, _ = templates.CurrentImports.Reserve(imp.ImportPath)
} else {
_, _ = templates.CurrentImports.Reserve(imp.ImportPath, imp.Alias)
}
}
return ""
}
type Resolver struct {
Object *codegen.Object
Field *codegen.Field
Implementation string
}
func gqlToResolverName(base string, gqlname, filenameTmpl string) string {
gqlname = filepath.Base(gqlname)
ext := filepath.Ext(gqlname)
if filenameTmpl == "" {
filenameTmpl = "{name}.resolvers.go"
}
filename := strings.ReplaceAll(filenameTmpl, "{name}", strings.TrimSuffix(gqlname, ext))
return filepath.Join(base, filename)
}