diff --git a/entgql/annotation.go b/entgql/annotation.go index c423e905f..1c0e3997a 100644 --- a/entgql/annotation.go +++ b/entgql/annotation.go @@ -25,6 +25,8 @@ import ( type ( // Annotation annotates fields and edges with metadata for templates. Annotation struct { + // Description is the description of the type or field. + Description string `json:"Description,omitempty"` // OrderField is the ordering field as defined in graphql schema. OrderField string `json:"OrderField,omitempty"` // MultiOrder indicates that orderBy should accept a list of OrderField terms. @@ -111,6 +113,10 @@ func (Annotation) Name() string { return "EntGQL" } +func Description(text string) Annotation { + return Annotation{Description: text} +} + // OrderField enables ordering in GraphQL for the annotated Ent field // with the given name. Note that, the field type must be comparable. // @@ -446,6 +452,9 @@ func (a Annotation) Merge(other schema.Annotation) schema.Annotation { default: return a } + if ant.Description != "" { + a.Description = ant.Description + } if ant.OrderField != "" { a.OrderField = ant.OrderField } diff --git a/entgql/annotation_test.go b/entgql/annotation_test.go index 99c9eebef..d71a6e29b 100644 --- a/entgql/annotation_test.go +++ b/entgql/annotation_test.go @@ -36,6 +36,10 @@ func TestAnnotation(t *testing.T) { annotation = entgql.MapsTo(names...) require.True(t, annotation.Unbind) require.ElementsMatch(t, names, annotation.Mapping) + + descr := "This is a test description" + annotation = entgql.Description(descr) + require.Equal(t, descr, annotation.Description) } func TestAnnotationDecode(t *testing.T) { @@ -45,17 +49,19 @@ func TestAnnotationDecode(t *testing.T) { require.Equal(t, ann, &entgql.Annotation{}) ann = &entgql.Annotation{} err = ann.Decode(map[string]interface{}{ - "OrderField": "NAME", - "Unbind": true, - "Mapping": []string{"f1", "f2"}, - "Skip": entgql.SkipAll, + "OrderField": "NAME", + "Unbind": true, + "Mapping": []string{"f1", "f2"}, + "Skip": entgql.SkipAll, + "Description": "This is a test description", }) require.NoError(t, err) require.Equal(t, ann, &entgql.Annotation{ - OrderField: "NAME", - Unbind: true, - Mapping: []string{"f1", "f2"}, - Skip: entgql.SkipAll, + OrderField: "NAME", + Unbind: true, + Mapping: []string{"f1", "f2"}, + Skip: entgql.SkipAll, + Description: "This is a test description", }) err = ann.Decode("invalid") require.NotNil(t, err) diff --git a/entgql/internal/todo/ent/schema/billproduct.go b/entgql/internal/todo/ent/schema/billproduct.go index 372c21f9e..3c1053019 100644 --- a/entgql/internal/todo/ent/schema/billproduct.go +++ b/entgql/internal/todo/ent/schema/billproduct.go @@ -38,6 +38,7 @@ func (BillProduct) Fields() []ent.Field { // Annotations returns BillProduct annotations. func (BillProduct) Annotations() []schema.Annotation { return []schema.Annotation{ + entgql.Description("BillProduct represents a product in a bill."), entgql.QueryField(), } } diff --git a/entgql/internal/todo/ent/schema/category.go b/entgql/internal/todo/ent/schema/category.go index b8251d21c..741f3b53c 100644 --- a/entgql/internal/todo/ent/schema/category.go +++ b/entgql/internal/todo/ent/schema/category.go @@ -97,6 +97,7 @@ func (Category) Edges() []ent.Edge { // Annotations returns Todo annotations. func (Category) Annotations() []schema.Annotation { return []schema.Annotation{ + entgql.Description("Category represents a category in the todo application."), entgql.QueryField(), entgql.RelayConnection(), entgql.Mutations(entgql.MutationCreate(), entgql.MutationUpdate()), diff --git a/entgql/internal/todoglobalid/ent.graphql b/entgql/internal/todoglobalid/ent.graphql index 8a75944e5..6bac522b3 100644 --- a/entgql/internal/todoglobalid/ent.graphql +++ b/entgql/internal/todoglobalid/ent.graphql @@ -1255,6 +1255,9 @@ type User implements Node { username: UUID! requiredMetadata: Map! metadata: Map + """ + The groups of the user + """ groups( """ Returns the elements in the list that come after the specified cursor. diff --git a/entgql/internal/todoglobalid/schema/group.go b/entgql/internal/todoglobalid/schema/group.go index 87ba62935..b7ea5f3b0 100644 --- a/entgql/internal/todoglobalid/schema/group.go +++ b/entgql/internal/todoglobalid/schema/group.go @@ -49,7 +49,8 @@ func (Group) Edges() []ent.Edge { func (Group) Annotations() []schema.Annotation { return []schema.Annotation{ entgql.RelayConnection(), - entgql.QueryField(), + entgql.QueryField(). + Description("The groups of the user"), entgql.Directives( annotation.HasPermissions([]string{"ADMIN", "MODERATOR"}), ), diff --git a/entgql/schema.go b/entgql/schema.go index ddf08a8a4..eb0b8b71e 100644 --- a/entgql/schema.go +++ b/entgql/schema.go @@ -219,7 +219,7 @@ func (e *schemaGenerator) buildTypes(g *gen.Graph, s *ast.Schema) error { _, hasOrderBy := s.Types[names.Order] hasWhereInput := e.genWhereInput && !ant.Skip.Is(SkipWhereInput) - def := names.ConnectionField(name, hasOrderBy, ant.MultiOrder, hasWhereInput) + def := names.ConnectionField(name, ant.Description, hasOrderBy, ant.MultiOrder, hasWhereInput) def.Description = ant.QueryField.Description def.Directives = e.buildDirectives(ant.QueryField.Directives) queryFields = append(queryFields, def) @@ -307,9 +307,10 @@ func (e *schemaGenerator) externalType(name string) bool { func (e *schemaGenerator) buildType(t *gen.Type, ant *Annotation, gqlType, pkg string) (*ast.Definition, error) { def := &ast.Definition{ - Name: gqlType, - Kind: ast.Object, - Directives: e.buildDirectives(ant.Directives), + Name: gqlType, + Kind: ast.Object, + Description: ant.Description, + Directives: e.buildDirectives(ant.Directives), } if t.Name != gqlType { def.Directives = append(def.Directives, goModel(entGoType(t.Name, pkg))) @@ -455,7 +456,7 @@ func (e *schemaGenerator) buildEdge(node *gen.Type, edge *gen.Edge, edgeAnt *Ann } fieldDef = paginationNames(gqlType). - ConnectionField(name, len(orderFields) > 0, ant.MultiOrder, + ConnectionField(name, edge.Comment(), len(orderFields) > 0, ant.MultiOrder, e.genWhereInput && !edgeAnt.Skip.Is(SkipWhereInput) && !ant.Skip.Is(SkipWhereInput), ) default: diff --git a/entgql/template.go b/entgql/template.go index 69f20dc3d..e5f0d7681 100644 --- a/entgql/template.go +++ b/entgql/template.go @@ -632,10 +632,11 @@ func (p *PaginationNames) OrderInputDef() *ast.Definition { } } -func (p *PaginationNames) ConnectionField(name string, hasOrderBy, multiOrder, hasWhereInput bool) *ast.FieldDefinition { +func (p *PaginationNames) ConnectionField(name, description string, hasOrderBy, multiOrder, hasWhereInput bool) *ast.FieldDefinition { def := &ast.FieldDefinition{ - Name: name, - Type: ast.NonNullNamedType(p.Connection, nil), + Name: name, + Description: description, + Type: ast.NonNullNamedType(p.Connection, nil), Arguments: ast.ArgumentDefinitionList{ { Name: "after", diff --git a/entgql/template_test.go b/entgql/template_test.go index 47701b136..6e46dad26 100644 --- a/entgql/template_test.go +++ b/entgql/template_test.go @@ -28,7 +28,9 @@ func TestFilterNodes(t *testing.T) { { Name: "Type1", Annotations: map[string]interface{}{ - annotationName: map[string]interface{}{}, + annotationName: map[string]interface{}{ + "Description": "This is a test description", + }, }, }, { @@ -47,7 +49,9 @@ func TestFilterNodes(t *testing.T) { { Name: "Type1", Annotations: map[string]interface{}{ - annotationName: map[string]interface{}{}, + annotationName: map[string]interface{}{ + "Description": "This is a test description", + }, }, }, { diff --git a/entgql/testdata/schema.graphql b/entgql/testdata/schema.graphql index 289e579b3..472317a84 100644 --- a/entgql/testdata/schema.graphql +++ b/entgql/testdata/schema.graphql @@ -1,9 +1,15 @@ +""" +BillProduct represents a product in a bill. +""" type BillProduct { id: ID! name: String! sku: String! quantity: Uint64! } +""" +Category represents a category in the todo application. +""" type Category { id: ID! text: String! diff --git a/entgql/testdata/schema_output.graphql b/entgql/testdata/schema_output.graphql index 289e579b3..472317a84 100644 --- a/entgql/testdata/schema_output.graphql +++ b/entgql/testdata/schema_output.graphql @@ -1,9 +1,15 @@ +""" +BillProduct represents a product in a bill. +""" type BillProduct { id: ID! name: String! sku: String! quantity: Uint64! } +""" +Category represents a category in the todo application. +""" type Category { id: ID! text: String! diff --git a/entgql/testdata/schema_relay.graphql b/entgql/testdata/schema_relay.graphql index 8f0d69b91..393de9a7d 100644 --- a/entgql/testdata/schema_relay.graphql +++ b/entgql/testdata/schema_relay.graphql @@ -1,3 +1,6 @@ +""" +BillProduct represents a product in a bill. +""" type BillProduct implements Node { id: ID! name: String! @@ -67,6 +70,9 @@ input BillProductWhereInput { quantityLT: Uint64 quantityLTE: Uint64 } +""" +Category represents a category in the todo application. +""" type Category implements Node { id: ID! text: String! @@ -1203,6 +1209,9 @@ type User implements Node { username: UUID! requiredMetadata: Map! metadata: Map + """ + The groups of the user + """ groups( """ Returns the elements in the list that come after the specified cursor. diff --git a/entgql/testdata/schema_relay_output.graphql b/entgql/testdata/schema_relay_output.graphql index 8f0d69b91..393de9a7d 100644 --- a/entgql/testdata/schema_relay_output.graphql +++ b/entgql/testdata/schema_relay_output.graphql @@ -1,3 +1,6 @@ +""" +BillProduct represents a product in a bill. +""" type BillProduct implements Node { id: ID! name: String! @@ -67,6 +70,9 @@ input BillProductWhereInput { quantityLT: Uint64 quantityLTE: Uint64 } +""" +Category represents a category in the todo application. +""" type Category implements Node { id: ID! text: String! @@ -1203,6 +1209,9 @@ type User implements Node { username: UUID! requiredMetadata: Map! metadata: Map + """ + The groups of the user + """ groups( """ Returns the elements in the list that come after the specified cursor.