github.com/Kartograf/gqlgen@v0.7.2/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) (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)
    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, 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) (*Object, error) {
    85  	obj := &Object{NamedType: types[typ.Name]}
    86  	typeEntry, entryExists := cfg.Models[typ.Name]
    87  
    88  	obj.ResolverInterface = &Ref{GoType: obj.GQLType + "Resolver"}
    89  
    90  	if typ == cfg.schema.Query {
    91  		obj.Root = true
    92  	}
    93  
    94  	if typ == cfg.schema.Mutation {
    95  		obj.Root = true
    96  		obj.DisableConcurrency = true
    97  	}
    98  
    99  	if typ == cfg.schema.Subscription {
   100  		obj.Root = true
   101  		obj.Stream = true
   102  	}
   103  
   104  	obj.Satisfies = append(obj.Satisfies, typ.Interfaces...)
   105  
   106  	for _, intf := range cfg.schema.GetImplements(typ) {
   107  		obj.Implements = append(obj.Implements, types[intf.Name])
   108  	}
   109  
   110  	for _, field := range typ.Fields {
   111  		if typ == cfg.schema.Query && field.Name == "__type" {
   112  			obj.Fields = append(obj.Fields, Field{
   113  				Type:           &Type{types["__Schema"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
   114  				GQLName:        "__schema",
   115  				GoFieldType:    GoFieldMethod,
   116  				GoReceiverName: "ec",
   117  				GoFieldName:    "introspectSchema",
   118  				Object:         obj,
   119  				Description:    field.Description,
   120  			})
   121  			continue
   122  		}
   123  		if typ == cfg.schema.Query && field.Name == "__schema" {
   124  			obj.Fields = append(obj.Fields, Field{
   125  				Type:           &Type{types["__Type"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
   126  				GQLName:        "__type",
   127  				GoFieldType:    GoFieldMethod,
   128  				GoReceiverName: "ec",
   129  				GoFieldName:    "introspectType",
   130  				Args: []FieldArgument{
   131  					{GQLName: "name", Type: &Type{types["String"], []string{}, ast.NamedType("String", nil), nil}, Object: &Object{}},
   132  				},
   133  				Object: obj,
   134  			})
   135  			continue
   136  		}
   137  
   138  		var forceResolver bool
   139  		var goName string
   140  		if entryExists {
   141  			if typeField, ok := typeEntry.Fields[field.Name]; ok {
   142  				goName = typeField.FieldName
   143  				forceResolver = typeField.Resolver
   144  			}
   145  		}
   146  
   147  		var args []FieldArgument
   148  		for _, arg := range field.Arguments {
   149  			newArg := FieldArgument{
   150  				GQLName:   arg.Name,
   151  				Type:      types.getType(arg.Type),
   152  				Object:    obj,
   153  				GoVarName: sanitizeArgName(arg.Name),
   154  			}
   155  
   156  			if !newArg.Type.IsInput && !newArg.Type.IsScalar {
   157  				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)
   158  			}
   159  
   160  			if arg.DefaultValue != nil {
   161  				var err error
   162  				newArg.Default, err = arg.DefaultValue.Value(nil)
   163  				if err != nil {
   164  					return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error())
   165  				}
   166  			}
   167  			args = append(args, newArg)
   168  		}
   169  
   170  		obj.Fields = append(obj.Fields, Field{
   171  			GQLName:       field.Name,
   172  			Type:          types.getType(field.Type),
   173  			Args:          args,
   174  			Object:        obj,
   175  			GoFieldName:   goName,
   176  			ForceResolver: forceResolver,
   177  		})
   178  	}
   179  
   180  	return obj, nil
   181  }