mirror of
https://github.com/stashapp/stash.git
synced 2025-12-15 04:44:28 +01:00
* Bump golang.org/x/text from 0.3.7 to 0.3.8 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.7 to 0.3.8. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.3.7...v0.3.8) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> * Update go dependencies * Update x/net --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
232 lines
6.4 KiB
Go
232 lines
6.4 KiB
Go
package resolvergen
|
|
|
|
import (
|
|
_ "embed"
|
|
"errors"
|
|
"fmt"
|
|
"go/ast"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/language"
|
|
|
|
"github.com/99designs/gqlgen/codegen"
|
|
"github.com/99designs/gqlgen/codegen/config"
|
|
"github.com/99designs/gqlgen/codegen/templates"
|
|
"github.com/99designs/gqlgen/graphql"
|
|
"github.com/99designs/gqlgen/internal/rewrite"
|
|
"github.com/99designs/gqlgen/plugin"
|
|
)
|
|
|
|
//go:embed resolver.gotpl
|
|
var resolverTemplate string
|
|
|
|
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 do not 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, nil, "// foo", `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,
|
|
Template: resolverTemplate,
|
|
})
|
|
}
|
|
|
|
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{}
|
|
|
|
objects := make(codegen.Objects, len(data.Objects)+len(data.Inputs))
|
|
copy(objects, data.Objects)
|
|
copy(objects[len(data.Objects):], data.Inputs)
|
|
|
|
for _, o := range 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{}
|
|
}
|
|
|
|
caser := cases.Title(language.English, cases.NoLower)
|
|
rewriter.MarkStructCopied(templates.LcFirst(o.Name) + templates.UcFirst(data.Config.Resolver.Type))
|
|
rewriter.GetMethodBody(data.Config.Resolver.Type, caser.String(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)
|
|
comment := strings.TrimSpace(strings.TrimLeft(rewriter.GetMethodComment(structName, f.GoFieldName), `\`))
|
|
if comment == "" {
|
|
comment = fmt.Sprintf("%v is the resolver for the %v field.", f.GoFieldName, f.Name)
|
|
}
|
|
implementation := strings.TrimSpace(rewriter.GetMethodBody(structName, f.GoFieldName))
|
|
if implementation == "" {
|
|
implementation = fmt.Sprintf("panic(fmt.Errorf(\"not implemented: %v - %v\"))", f.GoFieldName, f.Name)
|
|
}
|
|
|
|
resolver := Resolver{o, f, rewriter.GetPrevDecl(structName, f.GoFieldName), comment, 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.
|
|
// Code generated by github.com/99designs/gqlgen version ` + graphql.Version,
|
|
Filename: filename,
|
|
Data: resolverBuild,
|
|
Packages: data.Config.Packages,
|
|
Template: resolverTemplate,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, err := os.Stat(data.Config.Resolver.Filename); errors.Is(err, fs.ErrNotExist) {
|
|
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
|
|
PrevDecl *ast.FuncDecl
|
|
Comment string
|
|
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)
|
|
}
|