From acc45bf0bcda7e85be14d2489b193c1ed0228d4f Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Wed, 28 Feb 2018 19:14:51 +1100 Subject: [PATCH] generate interfaces --- codegen/build.go | 2 +- codegen/input_build.go | 2 +- codegen/interface_build.go | 48 ++++++++++++-------- codegen/model.go | 15 +++++++ codegen/models_build.go | 79 +++++++++++++++++++++++---------- codegen/object.go | 3 -- codegen/templates/data.go | 2 +- codegen/templates/models.gotpl | 20 +++++---- codegen/type.go | 1 + codegen/type_build.go | 2 + example/starwars/generated.go | 2 +- example/starwars/model.go | 9 ---- example/starwars/models_gen.go | 11 +++++ example/starwars/schema.graphql | 4 +- example/starwars/types.json | 3 -- 15 files changed, 132 insertions(+), 71 deletions(-) create mode 100644 codegen/model.go create mode 100644 example/starwars/models_gen.go diff --git a/codegen/build.go b/codegen/build.go index 4779ae80582..847cdebcced 100644 --- a/codegen/build.go +++ b/codegen/build.go @@ -25,7 +25,7 @@ type Build struct { type ModelBuild struct { PackageName string Imports Imports - Models Objects + Models []Model } // Create a list of models that need to be generated diff --git a/codegen/input_build.go b/codegen/input_build.go index 508d2853fa6..685c5df8558 100644 --- a/codegen/input_build.go +++ b/codegen/input_build.go @@ -40,7 +40,7 @@ func buildInputs(namedTypes NamedTypes, s *schema.Schema, prog *loader.Program, } func buildInput(types NamedTypes, typ *schema.InputObject) *Object { - obj := &Object{NamedType: types[typ.TypeName()], Input: true} + obj := &Object{NamedType: types[typ.TypeName()]} for _, field := range typ.Values { obj.Fields = append(obj.Fields, Field{ diff --git a/codegen/interface_build.go b/codegen/interface_build.go index 2b981e0bee5..b45d6ba7d18 100644 --- a/codegen/interface_build.go +++ b/codegen/interface_build.go @@ -1,6 +1,7 @@ package codegen import ( + "fmt" "sort" "strings" @@ -11,24 +12,10 @@ func buildInterfaces(types NamedTypes, s *schema.Schema) []*Interface { var interfaces []*Interface for _, typ := range s.Types { switch typ := typ.(type) { - - case *schema.Union: - i := &Interface{NamedType: types[typ.TypeName()]} - - for _, implementor := range typ.PossibleTypes { - i.Implementors = append(i.Implementors, types[implementor.TypeName()]) - } - - interfaces = append(interfaces, i) - - case *schema.Interface: - i := &Interface{NamedType: types[typ.TypeName()]} - - for _, implementor := range typ.PossibleTypes { - i.Implementors = append(i.Implementors, types[implementor.TypeName()]) - } - - interfaces = append(interfaces, i) + case *schema.Union, *schema.Interface: + interfaces = append(interfaces, buildInterface(types, typ)) + default: + continue } } @@ -38,3 +25,28 @@ func buildInterfaces(types NamedTypes, s *schema.Schema) []*Interface { return interfaces } + +func buildInterface(types NamedTypes, typ schema.NamedType) *Interface { + switch typ := typ.(type) { + + case *schema.Union: + i := &Interface{NamedType: types[typ.TypeName()]} + + for _, implementor := range typ.PossibleTypes { + i.Implementors = append(i.Implementors, types[implementor.TypeName()]) + } + + return i + + case *schema.Interface: + i := &Interface{NamedType: types[typ.TypeName()]} + + for _, implementor := range typ.PossibleTypes { + i.Implementors = append(i.Implementors, types[implementor.TypeName()]) + } + + return i + default: + panic(fmt.Errorf("unknown interface %#v", typ)) + } +} diff --git a/codegen/model.go b/codegen/model.go new file mode 100644 index 00000000000..d03f36194f3 --- /dev/null +++ b/codegen/model.go @@ -0,0 +1,15 @@ +package codegen + +type Model struct { + *NamedType + + Fields []ModelField +} + +type ModelField struct { + *Type + + GoVarName string + GoFKName string + GoFKType string +} diff --git a/codegen/models_build.go b/codegen/models_build.go index ee1521719d6..139020372fb 100644 --- a/codegen/models_build.go +++ b/codegen/models_build.go @@ -7,25 +7,34 @@ import ( "github.com/vektah/gqlgen/neelance/schema" ) -func buildModels(types NamedTypes, s *schema.Schema) Objects { - var models Objects +func buildModels(types NamedTypes, s *schema.Schema) []Model { + var models []Model for _, typ := range s.Types { - var model *Object + var model Model switch typ := typ.(type) { case *schema.Object: - model = buildObject(types, typ, s) - + obj := buildObject(types, typ, s) + if obj.Root || obj.GoType != "" { + continue + } + model = obj2Model(obj) case *schema.InputObject: - model = buildInput(types, typ) - } - - if model == nil || model.Root || model.GoType != "" { + obj := buildInput(types, typ) + if obj.GoType != "" { + continue + } + model = obj2Model(obj) + case *schema.Interface, *schema.Union: + intf := buildInterface(types, typ) + if intf.GoType != "" { + continue + } + model = int2Model(intf) + default: continue } - bindGenerated(types, model) - models = append(models, model) } @@ -36,23 +45,45 @@ func buildModels(types NamedTypes, s *schema.Schema) Objects { return models } -func bindGenerated(types NamedTypes, object *Object) { - object.GoType = ucFirst(object.GQLType) - object.Marshaler = &Ref{GoType: object.GoType} +func obj2Model(obj *Object) Model { + model := Model{ + NamedType: obj.NamedType, + Fields: []ModelField{}, + } + + model.GoType = ucFirst(obj.GQLType) + model.Marshaler = &Ref{GoType: obj.GoType} - for i := range object.Fields { - field := &object.Fields[i] + for i := range obj.Fields { + field := &obj.Fields[i] + mf := ModelField{Type: field.Type} - if field.IsScalar { - field.GoVarName = ucFirst(field.GQLName) - if field.GoVarName == "Id" { - field.GoVarName = "ID" + if mf.IsScalar { + mf.GoVarName = ucFirst(field.GQLName) + if mf.GoVarName == "Id" { + mf.GoVarName = "ID" } - } else if object.Input { - field.GoVarName = ucFirst(field.GQLName) + } else if mf.IsInput { + mf.GoVarName = ucFirst(field.GQLName) } else { - field.GoFKName = ucFirst(field.GQLName) + "ID" - field.GoFKType = "int" // todo: use schema to determine type of id? + mf.GoFKName = ucFirst(field.GQLName) + "ID" + mf.GoFKType = "int" // todo: use schema to determine type of id? } + + model.Fields = append(model.Fields, mf) + } + + return model +} + +func int2Model(obj *Interface) Model { + model := Model{ + NamedType: obj.NamedType, + Fields: []ModelField{}, } + + model.GoType = ucFirst(obj.GQLType) + model.Marshaler = &Ref{GoType: obj.GoType} + + return model } diff --git a/codegen/object.go b/codegen/object.go index e64bb3b59d0..67ded3532bb 100644 --- a/codegen/object.go +++ b/codegen/object.go @@ -17,7 +17,6 @@ type Object struct { Root bool DisableConcurrency bool Stream bool - Input bool } type Field struct { @@ -26,8 +25,6 @@ type Field struct { GQLName string // The name of the field in graphql GoMethodName string // The name of the method in go, if any GoVarName string // The name of the var in go, if any - GoFKName string // The name of the FK used when generating models - GoFKType string // The type of the FK used when generating models Args []FieldArgument // A list of arguments to be passed to this field NoErr bool // If this is bound to a go method, does that method have an error as the second argument Object *Object // A link back to the parent object diff --git a/codegen/templates/data.go b/codegen/templates/data.go index 21476ff6838..dafbe2855e1 100644 --- a/codegen/templates/data.go +++ b/codegen/templates/data.go @@ -6,6 +6,6 @@ var data = map[string]string{ "generated.gotpl": "// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\nfunc MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema {\n\treturn &executableSchema{resolvers}\n}\n\ntype Resolvers interface {\n{{- range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{ $field.ResolverDeclaration }}\n\t{{ end }}\n{{- end }}\n}\n\ntype executableSchema struct {\n\tresolvers Resolvers\n}\n\nfunc (e *executableSchema) Schema() *schema.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Query(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation, recover graphql.RecoverFunc) *graphql.Response {\n\t{{- if .QueryRoot }}\n\t\tec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx, recover: recover}\n\n\t\tdata := ec._{{.QueryRoot.GQLType}}(op.Selections)\n\t\tvar buf bytes.Buffer\n\t\tdata.MarshalGQL(&buf)\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf.Bytes(),\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn &graphql.Response{Errors: []*errors.QueryError{ {Message: \"queries are not supported\"} }}\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Mutation(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation, recover graphql.RecoverFunc) *graphql.Response {\n\t{{- if .MutationRoot }}\n\t\tec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx, recover: recover}\n\n\t\tdata := ec._{{.MutationRoot.GQLType}}(op.Selections)\n\t\tvar buf bytes.Buffer\n\t\tdata.MarshalGQL(&buf)\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf.Bytes(),\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn &graphql.Response{Errors: []*errors.QueryError{ {Message: \"mutations are not supported\"} }}\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Subscription(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation, recover graphql.RecoverFunc) func() *graphql.Response {\n\t{{- if .SubscriptionRoot }}\n\t\tec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx, recover: recover}\n\n\t\tnext := ec._{{.SubscriptionRoot.GQLType}}(op.Selections)\n\t\tif ec.Errors != nil {\n\t\t\treturn graphql.OneShot(&graphql.Response{Data: []byte(\"null\"), Errors: ec.Errors})\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\treturn func() *graphql.Response {\n\t\t\tbuf.Reset()\n\t\t\tdata := next()\n\t\t\tif data == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdata.MarshalGQL(&buf)\n\n\t\t\terrs := ec.Errors\n\t\t\tec.Errors = nil\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf.Bytes(),\n\t\t\t\tErrors: errs,\n\t\t\t}\n\t\t}\n\t{{- else }}\n\t\treturn graphql.OneShot(&graphql.Response{Errors: []*errors.QueryError{ {Message: \"subscriptions are not supported\"} }})\n\t{{- end }}\n}\n\ntype executionContext struct {\n\terrors.Builder\n\tresolvers Resolvers\n\tvariables map[string]interface{}\n\tdoc *query.Document\n\tctx context.Context\n\trecover graphql.RecoverFunc\n}\n\n{{- range $object := .Objects }}\n\t{{ template \"object.gotpl\" $object }}\n\n\t{{- range $field := $object.Fields }}\n\t\t{{ template \"field.gotpl\" $field }}\n\t{{ end }}\n{{- end}}\n\n{{- range $interface := .Interfaces }}\n\t{{ template \"interface.gotpl\" $interface }}\n{{- end }}\n\n{{- range $input := .Inputs }}\n\t{{ template \"input.gotpl\" $input }}\n{{- end }}\n\nvar parsedSchema = schema.MustParse({{.SchemaRaw|quote}})\n\nfunc (ec *executionContext) introspectSchema() *introspection.Schema {\n\treturn introspection.WrapSchema(parsedSchema)\n}\n\nfunc (ec *executionContext) introspectType(name string) *introspection.Type {\n\tt := parsedSchema.Resolve(name)\n\tif t == nil {\n\t\treturn nil\n\t}\n\treturn introspection.WrapType(t)\n}\n", "input.gotpl": "\t{{- if .IsMarshaled }}\n\tfunc Unmarshal{{ .GQLType }}(v interface{}) ({{.FullName}}, error) {\n\t\tvar it {{.FullName}}\n\n\t\tfor k, v := range v.(map[string]interface{}) {\n\t\t\tswitch k {\n\t\t\t{{- range $field := .Fields }}\n\t\t\tcase {{$field.GQLName|quote}}:\n\t\t\t\tvar err error\n\t\t\t\t{{ $field.Unmarshal (print \"it.\" $field.GoVarName) \"v\" }}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn it, err\n\t\t\t\t}\n\t\t\t{{- end }}\n\t\t\t}\n\t\t}\n\n\t\treturn it, nil\n\t}\n\t{{- end }}\n", "interface.gotpl": "{{- $interface := . }}\n\nfunc (ec *executionContext) _{{$interface.GQLType}}(sel []query.Selection, obj *{{$interface.FullName}}) graphql.Marshaler {\n\tswitch obj := (*obj).(type) {\n\tcase nil:\n\t\treturn graphql.Null\n\t{{- range $implementor := $interface.Implementors }}\n\tcase {{$implementor.FullName}}:\n\t\treturn ec._{{$implementor.GQLType}}(sel, &obj)\n\n\tcase *{{$implementor.FullName}}:\n\t\treturn ec._{{$implementor.GQLType}}(sel, obj)\n\n\t{{- end }}\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unexpected type %T\", obj))\n\t}\n}\n", - "models.gotpl": "// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\n{{ range $model := .Models }}\n\ttype {{.GoType}} struct {\n\t\t{{- range $field := .Fields }}\n\t\t\t{{- if $field.GoVarName }}\n\t\t\t\t{{ $field.GoVarName }} {{$field.Signature}}\n\t\t\t{{- else }}\n\t\t\t\t{{ $field.GoFKName }} {{$field.GoFKType}}\n\t\t\t{{- end }}\n\t\t{{- end }}\n\t}\n{{- end}}\n", + "models.gotpl": "// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\n{{ range $model := .Models }}\n\t{{- if .IsInterface }}\n\t\ttype {{.GoType}} interface {}\n\t{{- else }}\n\t\ttype {{.GoType}} struct {\n\t\t\t{{- range $field := .Fields }}\n\t\t\t\t{{- if $field.GoVarName }}\n\t\t\t\t\t{{ $field.GoVarName }} {{$field.Signature}}\n\t\t\t\t{{- else }}\n\t\t\t\t\t{{ $field.GoFKName }} {{$field.GoFKType}}\n\t\t\t\t{{- end }}\n\t\t\t{{- end }}\n\t\t}\n\t{{- end }}\n{{- end}}\n", "object.gotpl": "{{ $object := . }}\n\nvar {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}}\n\n// nolint: gocyclo, errcheck, gas, goconst\n{{- if .Stream }}\nfunc (ec *executionContext) _{{$object.GQLType}}(sel []query.Selection) func() graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.variables)\n\n\tif len(fields) != 1 {\n\t\tec.Errorf(\"must subscribe to exactly one stream\")\n\t\treturn nil\n\t}\n\n\tswitch fields[0].Name {\n\t{{- range $field := $object.Fields }}\n\tcase \"{{$field.GQLName}}\":\n\t\treturn ec._{{$object.GQLType}}_{{$field.GQLName}}(fields[0])\n\t{{- end }}\n\tdefault:\n\t\tpanic(\"unknown field \" + strconv.Quote(fields[0].Name))\n\t}\n}\n{{- else }}\nfunc (ec *executionContext) _{{$object.GQLType}}(sel []query.Selection{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.variables)\n\tout := graphql.NewOrderedMap(len(fields))\n\tfor i, field := range fields {\n\t\tout.Keys[i] = field.Alias\n\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString({{$object.GQLType|quote}})\n\t\t{{- range $field := $object.Fields }}\n\t\tcase \"{{$field.GQLName}}\":\n\t\t\tout.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(field{{if not $object.Root}}, obj{{end}})\n\t\t{{- end }}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\n\treturn out\n}\n{{- end }}\n", } diff --git a/codegen/templates/models.gotpl b/codegen/templates/models.gotpl index 857a960c19c..608c83791eb 100644 --- a/codegen/templates/models.gotpl +++ b/codegen/templates/models.gotpl @@ -9,13 +9,17 @@ import ( ) {{ range $model := .Models }} - type {{.GoType}} struct { - {{- range $field := .Fields }} - {{- if $field.GoVarName }} - {{ $field.GoVarName }} {{$field.Signature}} - {{- else }} - {{ $field.GoFKName }} {{$field.GoFKType}} + {{- if .IsInterface }} + type {{.GoType}} interface {} + {{- else }} + type {{.GoType}} struct { + {{- range $field := .Fields }} + {{- if $field.GoVarName }} + {{ $field.GoVarName }} {{$field.Signature}} + {{- else }} + {{ $field.GoFKName }} {{$field.GoFKType}} + {{- end }} {{- end }} - {{- end }} - } + } + {{- end }} {{- end}} diff --git a/codegen/type.go b/codegen/type.go index 9df6b2aba64..e28760b72fe 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -11,6 +11,7 @@ type NamedType struct { Ref IsScalar bool IsInterface bool + IsInput bool GQLType string // Name of the graphql type Marshaler *Ref // If this type has an external marshaler this will be set } diff --git a/codegen/type_build.go b/codegen/type_build.go index 95b66516d6b..387edecea21 100644 --- a/codegen/type_build.go +++ b/codegen/type_build.go @@ -57,6 +57,8 @@ func namedTypeFromSchema(schemaType schema.NamedType) *NamedType { return &NamedType{GQLType: val.TypeName(), IsScalar: true} case *schema.Interface, *schema.Union: return &NamedType{GQLType: val.TypeName(), IsInterface: true} + case *schema.InputObject: + return &NamedType{GQLType: val.TypeName(), IsInput: true} default: return &NamedType{GQLType: val.TypeName()} } diff --git a/example/starwars/generated.go b/example/starwars/generated.go index ca510ec6bcc..2b2c0a60a39 100644 --- a/example/starwars/generated.go +++ b/example/starwars/generated.go @@ -1583,7 +1583,7 @@ func UnmarshalReviewInput(v interface{}) (Review, error) { return it, nil } -var parsedSchema = schema.MustParse("schema {\n query: Query\n mutation: Mutation\n}\n# The query type, represents all of the entry points into our object graph\ntype Query {\n hero(episode: Episode = NEWHOPE): Character\n reviews(episode: Episode!, since: Time): [Review]!\n search(text: String!): [SearchResult]!\n character(id: ID!): Character\n droid(id: ID!): Droid\n human(id: ID!): Human\n starship(id: ID!): Starship\n}\n# The mutation type, represents all updates we can make to our data\ntype Mutation {\n createReview(episode: Episode!, review: ReviewInput!): Review\n}\n# The episodes in the Star Wars trilogy\nenum Episode {\n # Star Wars Episode IV: A New Hope, released in 1977.\n NEWHOPE\n # Star Wars Episode V: The Empire Strikes Back, released in 1980.\n EMPIRE\n # Star Wars Episode VI: Return of the Jedi, released in 1983.\n JEDI\n}\n# A character from the Star Wars universe\ninterface Character {\n # The ID of the character\n id: ID!\n # The name of the character\n name: String!\n # The friends of the character, or an empty list if they have none\n friends: [Character]\n # The friends of the character exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this character appears in\n appearsIn: [Episode!]!\n}\n# Units of height\nenum LengthUnit {\n # The standard unit around the world\n METER\n # Primarily used in the United States\n FOOT\n}\n# A humanoid creature from the Star Wars universe\ntype Human implements Character {\n # The ID of the human\n id: ID!\n # What this human calls themselves\n name: String!\n # Height in the preferred unit, default is meters\n height(unit: LengthUnit = METER): Float!\n # Mass in kilograms, or null if unknown\n mass: Float\n # This human's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the human exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this human appears in\n appearsIn: [Episode!]!\n # A list of starships this person has piloted, or an empty list if none\n starships: [Starship]\n}\n# An autonomous mechanical character in the Star Wars universe\ntype Droid implements Character {\n # The ID of the droid\n id: ID!\n # What others call this droid\n name: String!\n # This droid's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the droid exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this droid appears in\n appearsIn: [Episode!]!\n # This droid's primary function\n primaryFunction: String\n}\n# A connection object for a character's friends\ntype FriendsConnection {\n # The total number of friends\n totalCount: Int!\n # The edges for each of the character's friends.\n edges: [FriendsEdge]\n # A list of the friends, as a convenience when edges are not needed.\n friends: [Character]\n # Information for paginating this connection\n pageInfo: PageInfo!\n}\n# An edge object for a character's friends\ntype FriendsEdge {\n # A cursor used for pagination\n cursor: ID!\n # The character represented by this friendship edge\n node: Character\n}\n# Information for paginating this connection\ntype PageInfo {\n startCursor: ID\n endCursor: ID\n hasNextPage: Boolean!\n}\n# Represents a review for a movie\ntype Review {\n # The number of stars this review gave, 1-5\n stars: Int!\n # Comment about the movie\n commentary: String\n # when the review was posted\n time: Time\n}\n# The input object sent when someone is creating a new review\ninput ReviewInput {\n # 0-5 stars\n stars: Int!\n # Comment about the movie, optional\n commentary: String\n # when the review was posted\n time: Time\n}\ntype Starship {\n # The ID of the starship\n id: ID!\n # The name of the starship\n name: String!\n # Length of the starship, along the longest axis\n length(unit: LengthUnit = METER): Float!\n # coordinates tracking this ship\n history: [[Int]]\n}\nunion SearchResult = Human | Droid | Starship\nscalar Time\n") +var parsedSchema = schema.MustParse("schema {\n query: Query\n mutation: Mutation\n}\n# The query type, represents all of the entry points into our object graph\ntype Query {\n hero(episode: Episode = NEWHOPE): Character\n reviews(episode: Episode!, since: Time): [Review]!\n search(text: String!): [SearchResult]!\n character(id: ID!): Character\n droid(id: ID!): Droid\n human(id: ID!): Human\n starship(id: ID!): Starship\n}\n# The mutation type, represents all updates we can make to our data\ntype Mutation {\n createReview(episode: Episode!, review: ReviewInput!): Review\n}\n# The episodes in the Star Wars trilogy\nenum Episode {\n # Star Wars Episode IV: A New Hope, released in 1977.\n NEWHOPE\n # Star Wars Episode V: The Empire Strikes Back, released in 1980.\n EMPIRE\n # Star Wars Episode VI: Return of the Jedi, released in 1983.\n JEDI\n}\n# A character from the Star Wars universe\ninterface Character {\n # The ID of the character\n id: ID!\n # The name of the character\n name: String!\n # The friends of the character, or an empty list if they have none\n friends: [Character]\n # The friends of the character exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this character appears in\n appearsIn: [Episode!]!\n}\n# Units of height\nenum LengthUnit {\n # The standard unit around the world\n METER\n # Primarily used in the United States\n FOOT\n}\n# A humanoid creature from the Star Wars universe\ntype Human implements Character {\n # The ID of the human\n id: ID!\n # What this human calls themselves\n name: String!\n # Height in the preferred unit, default is meters\n height(unit: LengthUnit = METER): Float!\n # Mass in kilograms, or null if unknown\n mass: Float\n # This human's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the human exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this human appears in\n appearsIn: [Episode!]!\n # A list of starships this person has piloted, or an empty list if none\n starships: [Starship]\n}\n# An autonomous mechanical character in the Star Wars universe\ntype Droid implements Character {\n # The ID of the droid\n id: ID!\n # What others call this droid\n name: String!\n # This droid's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the droid exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this droid appears in\n appearsIn: [Episode!]!\n # This droid's primary function\n primaryFunction: String\n}\n# A connection object for a character's friends\ntype FriendsConnection {\n # The total number of friends\n totalCount: Int!\n # The edges for each of the character's friends.\n edges: [FriendsEdge]\n # A list of the friends, as a convenience when edges are not needed.\n friends: [Character]\n # Information for paginating this connection\n pageInfo: PageInfo!\n}\n# An edge object for a character's friends\ntype FriendsEdge {\n # A cursor used for pagination\n cursor: ID!\n # The character represented by this friendship edge\n node: Character\n}\n# Information for paginating this connection\ntype PageInfo {\n startCursor: ID!\n endCursor: ID!\n hasNextPage: Boolean!\n}\n# Represents a review for a movie\ntype Review {\n # The number of stars this review gave, 1-5\n stars: Int!\n # Comment about the movie\n commentary: String\n # when the review was posted\n time: Time\n}\n# The input object sent when someone is creating a new review\ninput ReviewInput {\n # 0-5 stars\n stars: Int!\n # Comment about the movie, optional\n commentary: String\n # when the review was posted\n time: Time\n}\ntype Starship {\n # The ID of the starship\n id: ID!\n # The name of the starship\n name: String!\n # Length of the starship, along the longest axis\n length(unit: LengthUnit = METER): Float!\n # coordinates tracking this ship\n history: [[Int]]\n}\nunion SearchResult = Human | Droid | Starship\nscalar Time\n") func (ec *executionContext) introspectSchema() *introspection.Schema { return introspection.WrapSchema(parsedSchema) diff --git a/example/starwars/model.go b/example/starwars/model.go index cdf3eeae91f..357f2e0054a 100644 --- a/example/starwars/model.go +++ b/example/starwars/model.go @@ -62,9 +62,6 @@ type Droid struct { PrimaryFunction string } -type Character interface{} -type SearchResult interface{} - func (r *Resolver) resolveFriendConnection(ctx context.Context, ids []string, first *int, after *string) (FriendsConnection, error) { from := 0 if after != nil { @@ -120,9 +117,3 @@ type FriendsEdge struct { Cursor string Node Character } - -type PageInfo struct { - StartCursor string - EndCursor string - HasNextPage bool -} diff --git a/example/starwars/models_gen.go b/example/starwars/models_gen.go new file mode 100644 index 00000000000..b8137a4dd63 --- /dev/null +++ b/example/starwars/models_gen.go @@ -0,0 +1,11 @@ +// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT + +package starwars + +type Character interface{} +type PageInfo struct { + StartCursor string + EndCursor string + HasNextPage bool +} +type SearchResult interface{} diff --git a/example/starwars/schema.graphql b/example/starwars/schema.graphql index 32b13a8e44a..f55b801c172 100644 --- a/example/starwars/schema.graphql +++ b/example/starwars/schema.graphql @@ -99,8 +99,8 @@ type FriendsEdge { } # Information for paginating this connection type PageInfo { - startCursor: ID - endCursor: ID + startCursor: ID! + endCursor: ID! hasNextPage: Boolean! } # Represents a review for a movie diff --git a/example/starwars/types.json b/example/starwars/types.json index 88c48ef1483..ce1985c81be 100644 --- a/example/starwars/types.json +++ b/example/starwars/types.json @@ -1,12 +1,9 @@ { - "Character": "github.com/vektah/gqlgen/example/starwars.Character", "Droid": "github.com/vektah/gqlgen/example/starwars.Droid", "FriendsConnection": "github.com/vektah/gqlgen/example/starwars.FriendsConnection", "FriendsEdge": "github.com/vektah/gqlgen/example/starwars.FriendsEdge", "Human": "github.com/vektah/gqlgen/example/starwars.Human", - "PageInfo": "github.com/vektah/gqlgen/example/starwars.PageInfo", "Review": "github.com/vektah/gqlgen/example/starwars.Review", "ReviewInput": "github.com/vektah/gqlgen/example/starwars.Review", - "SearchResult": "github.com/vektah/gqlgen/example/starwars.SearchResult", "Starship": "github.com/vektah/gqlgen/example/starwars.Starship" }