github.com/matiasanaya/gqlgen@v0.6.0/codegen/object_build.go (about)

     1  package codegen
     2  
     3  import (
     4  	"log"
     5  	"sort"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/vektah/gqlparser/ast"
     9  	"golang.org/x/tools/go/loader"
    10  )
    11  
    12  func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program, imports *Imports) (Objects, error) {
    13  	var objects Objects
    14  
    15  	for _, typ := range cfg.schema.Types {
    16  		if typ.Kind != ast.Object {
    17  			continue
    18  		}
    19  
    20  		obj, err := cfg.buildObject(types, typ, imports)
    21  		if err != nil {
    22  			return nil, err
    23  		}
    24  
    25  		def, err := findGoType(prog, obj.Package, obj.GoType)
    26  		if err != nil {
    27  			return nil, err
    28  		}
    29  		if def != nil {
    30  			for _, bindErr := range bindObject(def.Type(), obj, imports, cfg.StructTag) {
    31  				log.Println(bindErr.Error())
    32  				log.Println("  Adding resolver method")
    33  			}
    34  		}
    35  
    36  		objects = append(objects, obj)
    37  	}
    38  
    39  	sort.Slice(objects, func(i, j int) bool {
    40  		return objects[i].GQLType < objects[j].GQLType
    41  	})
    42  
    43  	return objects, nil
    44  }
    45  
    46  var keywords = []string{
    47  	"break",
    48  	"default",
    49  	"func",
    50  	"interface",
    51  	"select",
    52  	"case",
    53  	"defer",
    54  	"go",
    55  	"map",
    56  	"struct",
    57  	"chan",
    58  	"else",
    59  	"goto",
    60  	"package",
    61  	"switch",
    62  	"const",
    63  	"fallthrough",
    64  	"if",
    65  	"range",
    66  	"type",
    67  	"continue",
    68  	"for",
    69  	"import",
    70  	"return",
    71  	"var",
    72  }
    73  
    74  // sanitizeArgName prevents collisions with go keywords for arguments to resolver functions
    75  func sanitizeArgName(name string) string {
    76  	for _, k := range keywords {
    77  		if name == k {
    78  			return name + "Arg"
    79  		}
    80  	}
    81  	return name
    82  }
    83  
    84  func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *Imports) (*Object, error) {
    85  	obj := &Object{NamedType: types[typ.Name]}
    86  	typeEntry, entryExists := cfg.Models[typ.Name]
    87  
    88  	imp := imports.findByPath(cfg.Exec.ImportPath())
    89  	obj.ResolverInterface = &Ref{GoType: obj.GQLType + "Resolver", Import: imp}
    90  
    91  	if typ == cfg.schema.Query {
    92  		obj.Root = true
    93  	}
    94  
    95  	if typ == cfg.schema.Mutation {
    96  		obj.Root = true
    97  		obj.DisableConcurrency = true
    98  	}
    99  
   100  	if typ == cfg.schema.Subscription {
   101  		obj.Root = true
   102  		obj.Stream = true
   103  	}
   104  
   105  	obj.Satisfies = append(obj.Satisfies, typ.Interfaces...)
   106  
   107  	for _, intf := range cfg.schema.GetImplements(typ) {
   108  		obj.Implements = append(obj.Implements, types[intf.Name])
   109  	}
   110  
   111  	for _, field := range typ.Fields {
   112  		if typ == cfg.schema.Query && field.Name == "__type" {
   113  			obj.Fields = append(obj.Fields, Field{
   114  				Type:           &Type{types["__Schema"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
   115  				GQLName:        "__schema",
   116  				NoErr:          true,
   117  				GoFieldType:    GoFieldMethod,
   118  				GoReceiverName: "ec",
   119  				GoFieldName:    "introspectSchema",
   120  				Object:         obj,
   121  				Description:    field.Description,
   122  			})
   123  			continue
   124  		}
   125  		if typ == cfg.schema.Query && field.Name == "__schema" {
   126  			obj.Fields = append(obj.Fields, Field{
   127  				Type:           &Type{types["__Type"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
   128  				GQLName:        "__type",
   129  				NoErr:          true,
   130  				GoFieldType:    GoFieldMethod,
   131  				GoReceiverName: "ec",
   132  				GoFieldName:    "introspectType",
   133  				Args: []FieldArgument{
   134  					{GQLName: "name", Type: &Type{types["String"], []string{}, ast.NamedType("String", nil), nil}, Object: &Object{}},
   135  				},
   136  				Object: obj,
   137  			})
   138  			continue
   139  		}
   140  
   141  		var forceResolver bool
   142  		var goName string
   143  		if entryExists {
   144  			if typeField, ok := typeEntry.Fields[field.Name]; ok {
   145  				goName = typeField.FieldName
   146  				forceResolver = typeField.Resolver
   147  			}
   148  		}
   149  
   150  		var args []FieldArgument
   151  		for _, arg := range field.Arguments {
   152  			newArg := FieldArgument{
   153  				GQLName:   arg.Name,
   154  				Type:      types.getType(arg.Type),
   155  				Object:    obj,
   156  				GoVarName: sanitizeArgName(arg.Name),
   157  			}
   158  
   159  			if !newArg.Type.IsInput && !newArg.Type.IsScalar {
   160  				return nil, errors.Errorf("%s cannot be used as argument of %s.%s. only input and scalar types are allowed", arg.Type, obj.GQLType, field.Name)
   161  			}
   162  
   163  			if arg.DefaultValue != nil {
   164  				var err error
   165  				newArg.Default, err = arg.DefaultValue.Value(nil)
   166  				if err != nil {
   167  					return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error())
   168  				}
   169  			}
   170  			args = append(args, newArg)
   171  		}
   172  
   173  		obj.Fields = append(obj.Fields, Field{
   174  			GQLName:       field.Name,
   175  			Type:          types.getType(field.Type),
   176  			Args:          args,
   177  			Object:        obj,
   178  			GoFieldName:   goName,
   179  			ForceResolver: forceResolver,
   180  		})
   181  	}
   182  
   183  	return obj, nil
   184  }