graphql: directly return a connection, cleaning

Michael Muré created

Change summary

graphql/connections/connection_template.go |  82 ++++++++
graphql/connections/connections.go         |  39 ++++
graphql/connections/gen_bug.go             |  83 ++++++++
graphql/connections/gen_comment.go         |  83 ++++++++
graphql/connections/gen_operation.go       |  83 ++++++++
graphql/gqlgen.yml                         |   2 
graphql/models/edges.go                    |  13 +
graphql/models/gen_models.go               |   8 
graphql/resolvers/bug.go                   |  58 ++---
graphql/resolvers/gen_graph.go             | 118 ++++++------
graphql/resolvers/operations.go            |   9 
graphql/resolvers/pager_bug.go             | 225 ------------------------
graphql/resolvers/pager_comment.go         | 225 ------------------------
graphql/resolvers/pager_operation.go       | 225 ------------------------
graphql/resolvers/pagers.go                |  51 -----
graphql/resolvers/pagers_template.go       | 224 -----------------------
graphql/resolvers/repo.go                  |   3 
graphql/schema.graphql                     |   2 
18 files changed, 480 insertions(+), 1,053 deletions(-)

Detailed changes

graphql/connections/connection_template.go 🔗

@@ -0,0 +1,82 @@
+package connections
+
+import (
+	"fmt"
+	"github.com/MichaelMure/git-bug/graphql/models"
+	"github.com/cheekybits/genny/generic"
+)
+
+type NodeType generic.Type
+type EdgeType generic.Type
+type ConnectionType generic.Type
+
+type NodeTypeEdger func(value NodeType, offset int) Edge
+type NodeTypeConMaker func(edges []EdgeType, info models.PageInfo, totalCount int) ConnectionType
+
+func NodeTypeCon(source []NodeType, edger NodeTypeEdger, conMaker NodeTypeConMaker, input models.ConnectionInput) (ConnectionType, error) {
+	var edges []EdgeType
+	var pageInfo models.PageInfo
+
+	emptyCon := conMaker(edges, pageInfo, 0)
+
+	offset := 0
+
+	if input.After != nil {
+		for i, value := range source {
+			edge := edger(value, i)
+			if edge.GetCursor() == *input.After {
+				// remove all previous element including the "after" one
+				source = source[i+1:]
+				offset = i + 1
+				break
+			}
+		}
+	}
+
+	if input.Before != nil {
+		for i, value := range source {
+			edge := edger(value, i+offset)
+
+			if edge.GetCursor() == *input.Before {
+				// remove all after element including the "before" one
+				break
+			}
+
+			edges = append(edges, edge.(EdgeType))
+		}
+	} else {
+		edges = make([]EdgeType, len(source))
+
+		for i, value := range source {
+			edges[i] = edger(value, i+offset).(EdgeType)
+		}
+	}
+
+	if input.First != nil {
+		if *input.First < 0 {
+			return emptyCon, fmt.Errorf("first less than zero")
+		}
+
+		if len(edges) > *input.First {
+			// Slice result to be of length first by removing edges from the end
+			edges = edges[:*input.First]
+			pageInfo.HasNextPage = true
+		}
+	}
+
+	if input.Last != nil {
+		if *input.Last < 0 {
+			return emptyCon, fmt.Errorf("last less than zero")
+		}
+
+		if len(edges) > *input.Last {
+			// Slice result to be of length last by removing edges from the start
+			edges = edges[len(edges)-*input.Last:]
+			pageInfo.HasPreviousPage = true
+		}
+	}
+
+	con := conMaker(edges, pageInfo, len(source))
+
+	return con, nil
+}

graphql/connections/connections.go 🔗

@@ -0,0 +1,39 @@
+//go:generate genny -in=connection_template.go -out=gen_bug.go gen "NodeType=bug.Snapshot EdgeType=models.BugEdge ConnectionType=models.BugConnection"
+//go:generate genny -in=connection_template.go -out=gen_operation.go gen "NodeType=bug.Operation EdgeType=models.OperationEdge ConnectionType=models.OperationConnection"
+//go:generate genny -in=connection_template.go -out=gen_comment.go gen "NodeType=bug.Comment EdgeType=models.CommentEdge ConnectionType=models.CommentConnection"
+
+package connections
+
+import (
+	"encoding/base64"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+const cursorPrefix = "cursor:"
+
+type Edge interface {
+	GetCursor() string
+}
+
+// Creates the cursor string from an offset
+func OffsetToCursor(offset int) string {
+	str := fmt.Sprintf("%v%v", cursorPrefix, offset)
+	return base64.StdEncoding.EncodeToString([]byte(str))
+}
+
+// Re-derives the offset from the cursor string.
+func CursorToOffset(cursor string) (int, error) {
+	str := ""
+	b, err := base64.StdEncoding.DecodeString(cursor)
+	if err == nil {
+		str = string(b)
+	}
+	str = strings.Replace(str, cursorPrefix, "", -1)
+	offset, err := strconv.Atoi(str)
+	if err != nil {
+		return 0, fmt.Errorf("Invalid cursor")
+	}
+	return offset, nil
+}

graphql/connections/gen_bug.go 🔗

@@ -0,0 +1,83 @@
+// This file was automatically generated by genny.
+// Any changes will be lost if this file is regenerated.
+// see https://github.com/cheekybits/genny
+
+package connections
+
+import (
+	"fmt"
+
+	"github.com/MichaelMure/git-bug/bug"
+	"github.com/MichaelMure/git-bug/graphql/models"
+)
+
+type BugSnapshotEdger func(value bug.Snapshot, offset int) Edge
+type BugSnapshotConMaker func(edges []models.BugEdge, info models.PageInfo, totalCount int) models.BugConnection
+
+func BugSnapshotCon(source []bug.Snapshot, edger BugSnapshotEdger, conMaker BugSnapshotConMaker, input models.ConnectionInput) (models.BugConnection, error) {
+	var edges []models.BugEdge
+	var pageInfo models.PageInfo
+
+	emptyCon := conMaker(edges, pageInfo, 0)
+
+	offset := 0
+
+	if input.After != nil {
+		for i, value := range source {
+			edge := edger(value, i)
+			if edge.GetCursor() == *input.After {
+				// remove all previous element including the "after" one
+				source = source[i+1:]
+				offset = i + 1
+				break
+			}
+		}
+	}
+
+	if input.Before != nil {
+		for i, value := range source {
+			edge := edger(value, i+offset)
+
+			if edge.GetCursor() == *input.Before {
+				// remove all after element including the "before" one
+				break
+			}
+
+			edges = append(edges, edge.(models.BugEdge))
+		}
+	} else {
+		edges = make([]models.BugEdge, len(source))
+
+		for i, value := range source {
+			edges[i] = edger(value, i+offset).(models.BugEdge)
+		}
+	}
+
+	if input.First != nil {
+		if *input.First < 0 {
+			return emptyCon, fmt.Errorf("first less than zero")
+		}
+
+		if len(edges) > *input.First {
+			// Slice result to be of length first by removing edges from the end
+			edges = edges[:*input.First]
+			pageInfo.HasNextPage = true
+		}
+	}
+
+	if input.Last != nil {
+		if *input.Last < 0 {
+			return emptyCon, fmt.Errorf("last less than zero")
+		}
+
+		if len(edges) > *input.Last {
+			// Slice result to be of length last by removing edges from the start
+			edges = edges[len(edges)-*input.Last:]
+			pageInfo.HasPreviousPage = true
+		}
+	}
+
+	con := conMaker(edges, pageInfo, len(source))
+
+	return con, nil
+}

graphql/connections/gen_comment.go 🔗

@@ -0,0 +1,83 @@
+// This file was automatically generated by genny.
+// Any changes will be lost if this file is regenerated.
+// see https://github.com/cheekybits/genny
+
+package connections
+
+import (
+	"fmt"
+
+	"github.com/MichaelMure/git-bug/bug"
+	"github.com/MichaelMure/git-bug/graphql/models"
+)
+
+type BugCommentEdger func(value bug.Comment, offset int) Edge
+type BugCommentConMaker func(edges []models.CommentEdge, info models.PageInfo, totalCount int) models.CommentConnection
+
+func BugCommentCon(source []bug.Comment, edger BugCommentEdger, conMaker BugCommentConMaker, input models.ConnectionInput) (models.CommentConnection, error) {
+	var edges []models.CommentEdge
+	var pageInfo models.PageInfo
+
+	emptyCon := conMaker(edges, pageInfo, 0)
+
+	offset := 0
+
+	if input.After != nil {
+		for i, value := range source {
+			edge := edger(value, i)
+			if edge.GetCursor() == *input.After {
+				// remove all previous element including the "after" one
+				source = source[i+1:]
+				offset = i + 1
+				break
+			}
+		}
+	}
+
+	if input.Before != nil {
+		for i, value := range source {
+			edge := edger(value, i+offset)
+
+			if edge.GetCursor() == *input.Before {
+				// remove all after element including the "before" one
+				break
+			}
+
+			edges = append(edges, edge.(models.CommentEdge))
+		}
+	} else {
+		edges = make([]models.CommentEdge, len(source))
+
+		for i, value := range source {
+			edges[i] = edger(value, i+offset).(models.CommentEdge)
+		}
+	}
+
+	if input.First != nil {
+		if *input.First < 0 {
+			return emptyCon, fmt.Errorf("first less than zero")
+		}
+
+		if len(edges) > *input.First {
+			// Slice result to be of length first by removing edges from the end
+			edges = edges[:*input.First]
+			pageInfo.HasNextPage = true
+		}
+	}
+
+	if input.Last != nil {
+		if *input.Last < 0 {
+			return emptyCon, fmt.Errorf("last less than zero")
+		}
+
+		if len(edges) > *input.Last {
+			// Slice result to be of length last by removing edges from the start
+			edges = edges[len(edges)-*input.Last:]
+			pageInfo.HasPreviousPage = true
+		}
+	}
+
+	con := conMaker(edges, pageInfo, len(source))
+
+	return con, nil
+}

graphql/connections/gen_operation.go 🔗

@@ -0,0 +1,83 @@
+// This file was automatically generated by genny.
+// Any changes will be lost if this file is regenerated.
+// see https://github.com/cheekybits/genny
+
+package connections
+
+import (
+	"fmt"
+
+	"github.com/MichaelMure/git-bug/bug"
+	"github.com/MichaelMure/git-bug/graphql/models"
+)
+
+type BugOperationEdger func(value bug.Operation, offset int) Edge
+type BugOperationConMaker func(edges []models.OperationEdge, info models.PageInfo, totalCount int) models.OperationConnection
+
+func BugOperationCon(source []bug.Operation, edger BugOperationEdger, conMaker BugOperationConMaker, input models.ConnectionInput) (models.OperationConnection, error) {
+	var edges []models.OperationEdge
+	var pageInfo models.PageInfo
+
+	emptyCon := conMaker(edges, pageInfo, 0)
+
+	offset := 0
+
+	if input.After != nil {
+		for i, value := range source {
+			edge := edger(value, i)
+			if edge.GetCursor() == *input.After {
+				// remove all previous element including the "after" one
+				source = source[i+1:]
+				offset = i + 1
+				break
+			}
+		}
+	}
+
+	if input.Before != nil {
+		for i, value := range source {
+			edge := edger(value, i+offset)
+
+			if edge.GetCursor() == *input.Before {
+				// remove all after element including the "before" one
+				break
+			}
+
+			edges = append(edges, edge.(models.OperationEdge))
+		}
+	} else {
+		edges = make([]models.OperationEdge, len(source))
+
+		for i, value := range source {
+			edges[i] = edger(value, i+offset).(models.OperationEdge)
+		}
+	}
+
+	if input.First != nil {
+		if *input.First < 0 {
+			return emptyCon, fmt.Errorf("first less than zero")
+		}
+
+		if len(edges) > *input.First {
+			// Slice result to be of length first by removing edges from the end
+			edges = edges[:*input.First]
+			pageInfo.HasNextPage = true
+		}
+	}
+
+	if input.Last != nil {
+		if *input.Last < 0 {
+			return emptyCon, fmt.Errorf("last less than zero")
+		}
+
+		if len(edges) > *input.Last {
+			// Slice result to be of length last by removing edges from the start
+			edges = edges[len(edges)-*input.Last:]
+			pageInfo.HasPreviousPage = true
+		}
+	}
+
+	con := conMaker(edges, pageInfo, len(source))
+
+	return con, nil
+}

graphql/gqlgen.yml 🔗

@@ -2,7 +2,7 @@ schema: schema.graphql
 exec:
   filename: resolvers/gen_graph.go
 model:
-  filename: resolvers/gen_model.go
+  filename: models/gen_models.go
 
 models:
   Repository:

graphql/models/edges.go 🔗

@@ -0,0 +1,13 @@
+package models
+
+func (e OperationEdge) GetCursor() string {
+	return e.Cursor
+}
+
+func (e BugEdge) GetCursor() string {
+	return e.Cursor
+}
+
+func (e CommentEdge) GetCursor() string {
+	return e.Cursor
+}

graphql/resolvers/gen_model.go → graphql/models/gen_models.go 🔗

@@ -1,6 +1,6 @@
 // Code generated by github.com/vektah/gqlgen, DO NOT EDIT.
 
-package resolvers
+package models
 
 import (
 	fmt "fmt"
@@ -12,9 +12,9 @@ import (
 
 type Authored interface{}
 type BugConnection struct {
-	Edges      []*BugEdge `json:"edges"`
-	PageInfo   PageInfo   `json:"pageInfo"`
-	TotalCount int        `json:"totalCount"`
+	Edges      []BugEdge `json:"edges"`
+	PageInfo   PageInfo  `json:"pageInfo"`
+	TotalCount int       `json:"totalCount"`
 }
 type BugEdge struct {
 	Cursor string       `json:"cursor"`

graphql/resolvers/bug.go 🔗

@@ -4,58 +4,52 @@ import (
 	"context"
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/cache"
+	"github.com/MichaelMure/git-bug/graphql/connections"
+	"github.com/MichaelMure/git-bug/graphql/models"
 )
 
 type bugResolver struct {
 	cache cache.Cacher
 }
 
-func (bugResolver) Status(ctx context.Context, obj *bug.Snapshot) (Status, error) {
+func (bugResolver) Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error) {
 	return convertStatus(obj.Status)
 }
 
-func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (CommentConnection, error) {
-	var connection CommentConnection
-
-	edger := func(comment bug.Comment, offset int) Edge {
-		return CommentEdge{
+func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.CommentConnection, error) {
+	edger := func(comment bug.Comment, offset int) connections.Edge {
+		return models.CommentEdge{
 			Node:   comment,
-			Cursor: offsetToCursor(offset),
+			Cursor: connections.OffsetToCursor(offset),
 		}
 	}
 
-	edges, pageInfo, err := BugCommentPaginate(obj.Comments, edger, input)
-
-	if err != nil {
-		return connection, err
+	conMaker := func(edges []models.CommentEdge, info models.PageInfo, totalCount int) models.CommentConnection {
+		return models.CommentConnection{
+			Edges:      edges,
+			PageInfo:   info,
+			TotalCount: totalCount,
+		}
 	}
 
-	connection.Edges = edges
-	connection.PageInfo = pageInfo
-	connection.TotalCount = len(obj.Comments)
-
-	return connection, nil
+	return connections.BugCommentCon(obj.Comments, edger, conMaker, input)
 }
 
-func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (OperationConnection, error) {
-	var connection OperationConnection
-
-	edger := func(op bug.Operation, offset int) Edge {
-		return OperationEdge{
-			Node:   op.(OperationUnion),
-			Cursor: offsetToCursor(offset),
+func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.OperationConnection, error) {
+	edger := func(op bug.Operation, offset int) connections.Edge {
+		return models.OperationEdge{
+			Node:   op.(models.OperationUnion),
+			Cursor: connections.OffsetToCursor(offset),
 		}
 	}
 
-	edges, pageInfo, err := BugOperationPaginate(obj.Operations, edger, input)
-
-	if err != nil {
-		return connection, err
+	conMaker := func(edges []models.OperationEdge, info models.PageInfo, totalCount int) models.OperationConnection {
+		return models.OperationConnection{
+			Edges:      edges,
+			PageInfo:   info,
+			TotalCount: totalCount,
+		}
 	}
 
-	connection.Edges = edges
-	connection.PageInfo = pageInfo
-	connection.TotalCount = len(obj.Operations)
-
-	return connection, nil
+	return connections.BugOperationCon(obj.Operations, edger, conMaker, input)
 }

graphql/resolvers/gen_graph.go 🔗

@@ -11,6 +11,7 @@ import (
 
 	bug "github.com/MichaelMure/git-bug/bug"
 	operations "github.com/MichaelMure/git-bug/bug/operations"
+	models "github.com/MichaelMure/git-bug/graphql/models"
 	graphql "github.com/vektah/gqlgen/graphql"
 	introspection "github.com/vektah/gqlgen/neelance/introspection"
 	query "github.com/vektah/gqlgen/neelance/query"
@@ -30,10 +31,10 @@ func NewExecutableSchema(resolvers ResolverRoot) graphql.ExecutableSchema {
 type Resolvers interface {
 	AddCommentOperation_date(ctx context.Context, obj *operations.AddCommentOperation) (time.Time, error)
 
-	Bug_status(ctx context.Context, obj *bug.Snapshot) (Status, error)
+	Bug_status(ctx context.Context, obj *bug.Snapshot) (models.Status, error)
 
-	Bug_comments(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (CommentConnection, error)
-	Bug_operations(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (OperationConnection, error)
+	Bug_comments(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.CommentConnection, error)
+	Bug_operations(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.OperationConnection, error)
 
 	CreateOperation_date(ctx context.Context, obj *operations.CreateOperation) (time.Time, error)
 
@@ -42,11 +43,11 @@ type Resolvers interface {
 	Query_defaultRepository(ctx context.Context) (*repoResolver, error)
 	Query_repository(ctx context.Context, id string) (*repoResolver, error)
 
-	Repository_allBugs(ctx context.Context, obj *repoResolver, input ConnectionInput) (BugConnection, error)
+	Repository_allBugs(ctx context.Context, obj *repoResolver, input models.ConnectionInput) (models.BugConnection, error)
 	Repository_bug(ctx context.Context, obj *repoResolver, prefix string) (*bug.Snapshot, error)
 
 	SetStatusOperation_date(ctx context.Context, obj *operations.SetStatusOperation) (time.Time, error)
-	SetStatusOperation_status(ctx context.Context, obj *operations.SetStatusOperation) (Status, error)
+	SetStatusOperation_status(ctx context.Context, obj *operations.SetStatusOperation) (models.Status, error)
 
 	SetTitleOperation_date(ctx context.Context, obj *operations.SetTitleOperation) (time.Time, error)
 }
@@ -65,10 +66,10 @@ type AddCommentOperationResolver interface {
 	Date(ctx context.Context, obj *operations.AddCommentOperation) (time.Time, error)
 }
 type BugResolver interface {
-	Status(ctx context.Context, obj *bug.Snapshot) (Status, error)
+	Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error)
 
-	Comments(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (CommentConnection, error)
-	Operations(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (OperationConnection, error)
+	Comments(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.CommentConnection, error)
+	Operations(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.OperationConnection, error)
 }
 type CreateOperationResolver interface {
 	Date(ctx context.Context, obj *operations.CreateOperation) (time.Time, error)
@@ -81,12 +82,12 @@ type QueryResolver interface {
 	Repository(ctx context.Context, id string) (*repoResolver, error)
 }
 type RepositoryResolver interface {
-	AllBugs(ctx context.Context, obj *repoResolver, input ConnectionInput) (BugConnection, error)
+	AllBugs(ctx context.Context, obj *repoResolver, input models.ConnectionInput) (models.BugConnection, error)
 	Bug(ctx context.Context, obj *repoResolver, prefix string) (*bug.Snapshot, error)
 }
 type SetStatusOperationResolver interface {
 	Date(ctx context.Context, obj *operations.SetStatusOperation) (time.Time, error)
-	Status(ctx context.Context, obj *operations.SetStatusOperation) (Status, error)
+	Status(ctx context.Context, obj *operations.SetStatusOperation) (models.Status, error)
 }
 type SetTitleOperationResolver interface {
 	Date(ctx context.Context, obj *operations.SetTitleOperation) (time.Time, error)
@@ -100,15 +101,15 @@ func (s shortMapper) AddCommentOperation_date(ctx context.Context, obj *operatio
 	return s.r.AddCommentOperation().Date(ctx, obj)
 }
 
-func (s shortMapper) Bug_status(ctx context.Context, obj *bug.Snapshot) (Status, error) {
+func (s shortMapper) Bug_status(ctx context.Context, obj *bug.Snapshot) (models.Status, error) {
 	return s.r.Bug().Status(ctx, obj)
 }
 
-func (s shortMapper) Bug_comments(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (CommentConnection, error) {
+func (s shortMapper) Bug_comments(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.CommentConnection, error) {
 	return s.r.Bug().Comments(ctx, obj, input)
 }
 
-func (s shortMapper) Bug_operations(ctx context.Context, obj *bug.Snapshot, input ConnectionInput) (OperationConnection, error) {
+func (s shortMapper) Bug_operations(ctx context.Context, obj *bug.Snapshot, input models.ConnectionInput) (models.OperationConnection, error) {
 	return s.r.Bug().Operations(ctx, obj, input)
 }
 
@@ -128,7 +129,7 @@ func (s shortMapper) Query_repository(ctx context.Context, id string) (*repoReso
 	return s.r.Query().Repository(ctx, id)
 }
 
-func (s shortMapper) Repository_allBugs(ctx context.Context, obj *repoResolver, input ConnectionInput) (BugConnection, error) {
+func (s shortMapper) Repository_allBugs(ctx context.Context, obj *repoResolver, input models.ConnectionInput) (models.BugConnection, error) {
 	return s.r.Repository().AllBugs(ctx, obj, input)
 }
 
@@ -140,7 +141,7 @@ func (s shortMapper) SetStatusOperation_date(ctx context.Context, obj *operation
 	return s.r.SetStatusOperation().Date(ctx, obj)
 }
 
-func (s shortMapper) SetStatusOperation_status(ctx context.Context, obj *operations.SetStatusOperation) (Status, error) {
+func (s shortMapper) SetStatusOperation_status(ctx context.Context, obj *operations.SetStatusOperation) (models.Status, error) {
 	return s.r.SetStatusOperation().Status(ctx, obj)
 }
 
@@ -358,7 +359,7 @@ func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.Colle
 		if resTmp == nil {
 			return graphql.Null
 		}
-		res := resTmp.(Status)
+		res := resTmp.(models.Status)
 		return res
 	})
 }
@@ -385,7 +386,7 @@ func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.Colle
 
 func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) graphql.Marshaler {
 	args := map[string]interface{}{}
-	var arg0 ConnectionInput
+	var arg0 models.ConnectionInput
 	if tmp, ok := field.Args["input"]; ok {
 		var err error
 		arg0, err = UnmarshalConnectionInput(tmp)
@@ -410,7 +411,7 @@ func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.Col
 		}()
 
 		resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
-			return ec.resolvers.Bug_comments(ctx, obj, args["input"].(ConnectionInput))
+			return ec.resolvers.Bug_comments(ctx, obj, args["input"].(models.ConnectionInput))
 		})
 		if err != nil {
 			ec.Error(ctx, err)
@@ -419,14 +420,14 @@ func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.Col
 		if resTmp == nil {
 			return graphql.Null
 		}
-		res := resTmp.(CommentConnection)
+		res := resTmp.(models.CommentConnection)
 		return ec._CommentConnection(ctx, field.Selections, &res)
 	})
 }
 
 func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) graphql.Marshaler {
 	args := map[string]interface{}{}
-	var arg0 ConnectionInput
+	var arg0 models.ConnectionInput
 	if tmp, ok := field.Args["input"]; ok {
 		var err error
 		arg0, err = UnmarshalConnectionInput(tmp)
@@ -451,7 +452,7 @@ func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.C
 		}()
 
 		resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
-			return ec.resolvers.Bug_operations(ctx, obj, args["input"].(ConnectionInput))
+			return ec.resolvers.Bug_operations(ctx, obj, args["input"].(models.ConnectionInput))
 		})
 		if err != nil {
 			ec.Error(ctx, err)
@@ -460,7 +461,7 @@ func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.C
 		if resTmp == nil {
 			return graphql.Null
 		}
-		res := resTmp.(OperationConnection)
+		res := resTmp.(models.OperationConnection)
 		return ec._OperationConnection(ctx, field.Selections, &res)
 	})
 }
@@ -468,7 +469,7 @@ func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.C
 var bugConnectionImplementors = []string{"BugConnection"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _BugConnection(ctx context.Context, sel []query.Selection, obj *BugConnection) graphql.Marshaler {
+func (ec *executionContext) _BugConnection(ctx context.Context, sel []query.Selection, obj *models.BugConnection) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, bugConnectionImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -492,7 +493,7 @@ func (ec *executionContext) _BugConnection(ctx context.Context, sel []query.Sele
 	return out
 }
 
-func (ec *executionContext) _BugConnection_edges(ctx context.Context, field graphql.CollectedField, obj *BugConnection) graphql.Marshaler {
+func (ec *executionContext) _BugConnection_edges(ctx context.Context, field graphql.CollectedField, obj *models.BugConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "BugConnection"
 	rctx.Args = nil
@@ -506,16 +507,13 @@ func (ec *executionContext) _BugConnection_edges(ctx context.Context, field grap
 			rctx := graphql.GetResolverContext(ctx)
 			rctx.PushIndex(idx1)
 			defer rctx.Pop()
-			if res[idx1] == nil {
-				return graphql.Null
-			}
-			return ec._BugEdge(ctx, field.Selections, res[idx1])
+			return ec._BugEdge(ctx, field.Selections, &res[idx1])
 		}())
 	}
 	return arr1
 }
 
-func (ec *executionContext) _BugConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *BugConnection) graphql.Marshaler {
+func (ec *executionContext) _BugConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.BugConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "BugConnection"
 	rctx.Args = nil
@@ -526,7 +524,7 @@ func (ec *executionContext) _BugConnection_pageInfo(ctx context.Context, field g
 	return ec._PageInfo(ctx, field.Selections, &res)
 }
 
-func (ec *executionContext) _BugConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *BugConnection) graphql.Marshaler {
+func (ec *executionContext) _BugConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.BugConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "BugConnection"
 	rctx.Args = nil
@@ -540,7 +538,7 @@ func (ec *executionContext) _BugConnection_totalCount(ctx context.Context, field
 var bugEdgeImplementors = []string{"BugEdge"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _BugEdge(ctx context.Context, sel []query.Selection, obj *BugEdge) graphql.Marshaler {
+func (ec *executionContext) _BugEdge(ctx context.Context, sel []query.Selection, obj *models.BugEdge) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, bugEdgeImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -562,7 +560,7 @@ func (ec *executionContext) _BugEdge(ctx context.Context, sel []query.Selection,
 	return out
 }
 
-func (ec *executionContext) _BugEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *BugEdge) graphql.Marshaler {
+func (ec *executionContext) _BugEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *models.BugEdge) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "BugEdge"
 	rctx.Args = nil
@@ -573,7 +571,7 @@ func (ec *executionContext) _BugEdge_cursor(ctx context.Context, field graphql.C
 	return graphql.MarshalString(res)
 }
 
-func (ec *executionContext) _BugEdge_node(ctx context.Context, field graphql.CollectedField, obj *BugEdge) graphql.Marshaler {
+func (ec *executionContext) _BugEdge_node(ctx context.Context, field graphql.CollectedField, obj *models.BugEdge) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "BugEdge"
 	rctx.Args = nil
@@ -634,7 +632,7 @@ func (ec *executionContext) _Comment_message(ctx context.Context, field graphql.
 var commentConnectionImplementors = []string{"CommentConnection"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _CommentConnection(ctx context.Context, sel []query.Selection, obj *CommentConnection) graphql.Marshaler {
+func (ec *executionContext) _CommentConnection(ctx context.Context, sel []query.Selection, obj *models.CommentConnection) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, commentConnectionImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -658,7 +656,7 @@ func (ec *executionContext) _CommentConnection(ctx context.Context, sel []query.
 	return out
 }
 
-func (ec *executionContext) _CommentConnection_edges(ctx context.Context, field graphql.CollectedField, obj *CommentConnection) graphql.Marshaler {
+func (ec *executionContext) _CommentConnection_edges(ctx context.Context, field graphql.CollectedField, obj *models.CommentConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "CommentConnection"
 	rctx.Args = nil
@@ -678,7 +676,7 @@ func (ec *executionContext) _CommentConnection_edges(ctx context.Context, field
 	return arr1
 }
 
-func (ec *executionContext) _CommentConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *CommentConnection) graphql.Marshaler {
+func (ec *executionContext) _CommentConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.CommentConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "CommentConnection"
 	rctx.Args = nil
@@ -689,7 +687,7 @@ func (ec *executionContext) _CommentConnection_pageInfo(ctx context.Context, fie
 	return ec._PageInfo(ctx, field.Selections, &res)
 }
 
-func (ec *executionContext) _CommentConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *CommentConnection) graphql.Marshaler {
+func (ec *executionContext) _CommentConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.CommentConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "CommentConnection"
 	rctx.Args = nil
@@ -703,7 +701,7 @@ func (ec *executionContext) _CommentConnection_totalCount(ctx context.Context, f
 var commentEdgeImplementors = []string{"CommentEdge"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _CommentEdge(ctx context.Context, sel []query.Selection, obj *CommentEdge) graphql.Marshaler {
+func (ec *executionContext) _CommentEdge(ctx context.Context, sel []query.Selection, obj *models.CommentEdge) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, commentEdgeImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -725,7 +723,7 @@ func (ec *executionContext) _CommentEdge(ctx context.Context, sel []query.Select
 	return out
 }
 
-func (ec *executionContext) _CommentEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *CommentEdge) graphql.Marshaler {
+func (ec *executionContext) _CommentEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *models.CommentEdge) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "CommentEdge"
 	rctx.Args = nil
@@ -736,7 +734,7 @@ func (ec *executionContext) _CommentEdge_cursor(ctx context.Context, field graph
 	return graphql.MarshalString(res)
 }
 
-func (ec *executionContext) _CommentEdge_node(ctx context.Context, field graphql.CollectedField, obj *CommentEdge) graphql.Marshaler {
+func (ec *executionContext) _CommentEdge_node(ctx context.Context, field graphql.CollectedField, obj *models.CommentEdge) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "CommentEdge"
 	rctx.Args = nil
@@ -952,7 +950,7 @@ func (ec *executionContext) _LabelChangeOperation_removed(ctx context.Context, f
 var operationConnectionImplementors = []string{"OperationConnection"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _OperationConnection(ctx context.Context, sel []query.Selection, obj *OperationConnection) graphql.Marshaler {
+func (ec *executionContext) _OperationConnection(ctx context.Context, sel []query.Selection, obj *models.OperationConnection) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, operationConnectionImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -976,7 +974,7 @@ func (ec *executionContext) _OperationConnection(ctx context.Context, sel []quer
 	return out
 }
 
-func (ec *executionContext) _OperationConnection_edges(ctx context.Context, field graphql.CollectedField, obj *OperationConnection) graphql.Marshaler {
+func (ec *executionContext) _OperationConnection_edges(ctx context.Context, field graphql.CollectedField, obj *models.OperationConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "OperationConnection"
 	rctx.Args = nil
@@ -996,7 +994,7 @@ func (ec *executionContext) _OperationConnection_edges(ctx context.Context, fiel
 	return arr1
 }
 
-func (ec *executionContext) _OperationConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *OperationConnection) graphql.Marshaler {
+func (ec *executionContext) _OperationConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.OperationConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "OperationConnection"
 	rctx.Args = nil
@@ -1007,7 +1005,7 @@ func (ec *executionContext) _OperationConnection_pageInfo(ctx context.Context, f
 	return ec._PageInfo(ctx, field.Selections, &res)
 }
 
-func (ec *executionContext) _OperationConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *OperationConnection) graphql.Marshaler {
+func (ec *executionContext) _OperationConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.OperationConnection) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "OperationConnection"
 	rctx.Args = nil
@@ -1021,7 +1019,7 @@ func (ec *executionContext) _OperationConnection_totalCount(ctx context.Context,
 var operationEdgeImplementors = []string{"OperationEdge"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _OperationEdge(ctx context.Context, sel []query.Selection, obj *OperationEdge) graphql.Marshaler {
+func (ec *executionContext) _OperationEdge(ctx context.Context, sel []query.Selection, obj *models.OperationEdge) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, operationEdgeImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -1043,7 +1041,7 @@ func (ec *executionContext) _OperationEdge(ctx context.Context, sel []query.Sele
 	return out
 }
 
-func (ec *executionContext) _OperationEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *OperationEdge) graphql.Marshaler {
+func (ec *executionContext) _OperationEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *models.OperationEdge) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "OperationEdge"
 	rctx.Args = nil
@@ -1054,7 +1052,7 @@ func (ec *executionContext) _OperationEdge_cursor(ctx context.Context, field gra
 	return graphql.MarshalString(res)
 }
 
-func (ec *executionContext) _OperationEdge_node(ctx context.Context, field graphql.CollectedField, obj *OperationEdge) graphql.Marshaler {
+func (ec *executionContext) _OperationEdge_node(ctx context.Context, field graphql.CollectedField, obj *models.OperationEdge) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "OperationEdge"
 	rctx.Args = nil
@@ -1068,7 +1066,7 @@ func (ec *executionContext) _OperationEdge_node(ctx context.Context, field graph
 var pageInfoImplementors = []string{"PageInfo"}
 
 // nolint: gocyclo, errcheck, gas, goconst
-func (ec *executionContext) _PageInfo(ctx context.Context, sel []query.Selection, obj *PageInfo) graphql.Marshaler {
+func (ec *executionContext) _PageInfo(ctx context.Context, sel []query.Selection, obj *models.PageInfo) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.Doc, sel, pageInfoImplementors, ec.Variables)
 
 	out := graphql.NewOrderedMap(len(fields))
@@ -1090,7 +1088,7 @@ func (ec *executionContext) _PageInfo(ctx context.Context, sel []query.Selection
 	return out
 }
 
-func (ec *executionContext) _PageInfo_hasNextPage(ctx context.Context, field graphql.CollectedField, obj *PageInfo) graphql.Marshaler {
+func (ec *executionContext) _PageInfo_hasNextPage(ctx context.Context, field graphql.CollectedField, obj *models.PageInfo) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "PageInfo"
 	rctx.Args = nil
@@ -1101,7 +1099,7 @@ func (ec *executionContext) _PageInfo_hasNextPage(ctx context.Context, field gra
 	return graphql.MarshalBoolean(res)
 }
 
-func (ec *executionContext) _PageInfo_hasPreviousPage(ctx context.Context, field graphql.CollectedField, obj *PageInfo) graphql.Marshaler {
+func (ec *executionContext) _PageInfo_hasPreviousPage(ctx context.Context, field graphql.CollectedField, obj *models.PageInfo) graphql.Marshaler {
 	rctx := graphql.GetResolverContext(ctx)
 	rctx.Object = "PageInfo"
 	rctx.Args = nil
@@ -1335,7 +1333,7 @@ func (ec *executionContext) _Repository(ctx context.Context, sel []query.Selecti
 
 func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graphql.CollectedField, obj *repoResolver) graphql.Marshaler {
 	args := map[string]interface{}{}
-	var arg0 ConnectionInput
+	var arg0 models.ConnectionInput
 	if tmp, ok := field.Args["input"]; ok {
 		var err error
 		arg0, err = UnmarshalConnectionInput(tmp)
@@ -1360,7 +1358,7 @@ func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graph
 		}()
 
 		resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
-			return ec.resolvers.Repository_allBugs(ctx, obj, args["input"].(ConnectionInput))
+			return ec.resolvers.Repository_allBugs(ctx, obj, args["input"].(models.ConnectionInput))
 		})
 		if err != nil {
 			ec.Error(ctx, err)
@@ -1369,7 +1367,7 @@ func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graph
 		if resTmp == nil {
 			return graphql.Null
 		}
-		res := resTmp.(BugConnection)
+		res := resTmp.(models.BugConnection)
 		return ec._BugConnection(ctx, field.Selections, &res)
 	})
 }
@@ -1511,7 +1509,7 @@ func (ec *executionContext) _SetStatusOperation_status(ctx context.Context, fiel
 		if resTmp == nil {
 			return graphql.Null
 		}
-		res := resTmp.(Status)
+		res := resTmp.(models.Status)
 		return res
 	})
 }
@@ -2285,7 +2283,7 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
 	return ec.___Type(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Authored(ctx context.Context, sel []query.Selection, obj *Authored) graphql.Marshaler {
+func (ec *executionContext) _Authored(ctx context.Context, sel []query.Selection, obj *models.Authored) graphql.Marshaler {
 	switch obj := (*obj).(type) {
 	case nil:
 		return graphql.Null
@@ -2318,7 +2316,7 @@ func (ec *executionContext) _Authored(ctx context.Context, sel []query.Selection
 	}
 }
 
-func (ec *executionContext) _Operation(ctx context.Context, sel []query.Selection, obj *Operation) graphql.Marshaler {
+func (ec *executionContext) _Operation(ctx context.Context, sel []query.Selection, obj *models.Operation) graphql.Marshaler {
 	switch obj := (*obj).(type) {
 	case nil:
 		return graphql.Null
@@ -2347,7 +2345,7 @@ func (ec *executionContext) _Operation(ctx context.Context, sel []query.Selectio
 	}
 }
 
-func (ec *executionContext) _OperationUnion(ctx context.Context, sel []query.Selection, obj *OperationUnion) graphql.Marshaler {
+func (ec *executionContext) _OperationUnion(ctx context.Context, sel []query.Selection, obj *models.OperationUnion) graphql.Marshaler {
 	switch obj := (*obj).(type) {
 	case nil:
 		return graphql.Null
@@ -2376,8 +2374,8 @@ func (ec *executionContext) _OperationUnion(ctx context.Context, sel []query.Sel
 	}
 }
 
-func UnmarshalConnectionInput(v interface{}) (ConnectionInput, error) {
-	var it ConnectionInput
+func UnmarshalConnectionInput(v interface{}) (models.ConnectionInput, error) {
+	var it models.ConnectionInput
 	var asMap = v.(map[string]interface{})
 
 	for k, v := range asMap {
@@ -2584,7 +2582,7 @@ union OperationUnion =
 # The connection type for Bug.
 type BugConnection {
   # A list of edges.
-  edges: [BugEdge]!
+  edges: [BugEdge!]!
 
   # Information to aid in pagination.
   pageInfo: PageInfo!

graphql/resolvers/operations.go 🔗

@@ -6,6 +6,7 @@ import (
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/bug/operations"
 	"time"
+	"github.com/MichaelMure/git-bug/graphql/models"
 )
 
 type addCommentOperationResolver struct{}
@@ -32,7 +33,7 @@ func (setStatusOperationResolver) Date(ctx context.Context, obj *operations.SetS
 	return obj.Time(), nil
 }
 
-func (setStatusOperationResolver) Status(ctx context.Context, obj *operations.SetStatusOperation) (Status, error) {
+func (setStatusOperationResolver) Status(ctx context.Context, obj *operations.SetStatusOperation) (models.Status, error) {
 	return convertStatus(obj.Status)
 }
 
@@ -42,12 +43,12 @@ func (setTitleOperationResolver) Date(ctx context.Context, obj *operations.SetTi
 	return obj.Time(), nil
 }
 
-func convertStatus(status bug.Status) (Status, error) {
+func convertStatus(status bug.Status) (models.Status, error) {
 	switch status {
 	case bug.OpenStatus:
-		return StatusOpen, nil
+		return models.StatusOpen, nil
 	case bug.ClosedStatus:
-		return StatusClosed, nil
+		return models.StatusClosed, nil
 	}
 
 	return "", fmt.Errorf("Unknown status")

graphql/resolvers/pager_bug.go 🔗

@@ -1,225 +0,0 @@
-// This file was automatically generated by genny.
-// Any changes will be lost if this file is regenerated.
-// see https://github.com/cheekybits/genny
-
-package resolvers
-
-import (
-	"fmt"
-
-	"github.com/MichaelMure/git-bug/bug"
-)
-
-type BugSnapshotEdger func(value bug.Snapshot, offset int) Edge
-
-func BugSnapshotPaginate(source []bug.Snapshot, edger BugSnapshotEdger, input ConnectionInput) ([]BugEdge, PageInfo, error) {
-	var result []BugEdge
-	var pageInfo PageInfo
-
-	offset := 0
-
-	if input.After != nil {
-		for i, value := range source {
-			edge := edger(value, i)
-			if edge.GetCursor() == *input.After {
-				// remove all previous element including the "after" one
-				source = source[i+1:]
-				offset = i + 1
-				break
-			}
-		}
-	}
-
-	if input.Before != nil {
-		for i, value := range source {
-			edge := edger(value, i+offset)
-
-			if edge.GetCursor() == *input.Before {
-				// remove all after element including the "before" one
-				break
-			}
-
-			result = append(result, edge.(BugEdge))
-		}
-	} else {
-		result = make([]BugEdge, len(source))
-
-		for i, value := range source {
-			result[i] = edger(value, i+offset).(BugEdge)
-		}
-	}
-
-	if input.First != nil {
-		if *input.First < 0 {
-			return nil, PageInfo{}, fmt.Errorf("first less than zero")
-		}
-
-		if len(result) > *input.First {
-			// Slice result to be of length first by removing edges from the end
-			result = result[:*input.First]
-			pageInfo.HasNextPage = true
-		}
-	}
-
-	if input.Last != nil {
-		if *input.Last < 0 {
-			return nil, PageInfo{}, fmt.Errorf("last less than zero")
-		}
-
-		if len(result) > *input.Last {
-			// Slice result to be of length last by removing edges from the start
-			result = result[len(result)-*input.Last:]
-			pageInfo.HasPreviousPage = true
-		}
-	}
-
-	return result, pageInfo, nil
-}
-
-// Apply the before/after cursor params to the source and return an array of edges
-//func ApplyCursorToEdges(source []interface{}, edger Edger, input ConnectionInput) []Edge {
-//	var result []Edge
-//
-//	if input.After != nil {
-//		for i, value := range source {
-//			edge := edger(value)
-//			if edge.Cursor() == *input.After {
-//				// remove all previous element including the "after" one
-//				source = source[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if input.Before != nil {
-//		for _, value := range source {
-//			edge := edger(value)
-//
-//			if edge.Cursor() == *input.Before {
-//				// remove all after element including the "before" one
-//				break
-//			}
-//
-//			result = append(result, edge)
-//		}
-//	} else {
-//		result = make([]Edge, len(source))
-//
-//		for i, value := range source {
-//			result[i] = edger(value)
-//		}
-//	}
-//
-//	return result
-//}
-
-// Apply the first/last cursor params to the edges
-//func EdgesToReturn(edges []Edge, input ConnectionInput) ([]Edge, PageInfo, error) {
-//	hasPreviousPage := false
-//	hasNextPage := false
-//
-//	if input.First != nil {
-//		if *input.First < 0 {
-//			return nil, nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(edges) > *input.First {
-//			// Slice result to be of length first by removing edges from the end
-//			edges = edges[:*input.First]
-//			hasNextPage = true
-//		}
-//	}
-//
-//	if input.Last != nil {
-//		if *input.Last < 0 {
-//			return nil, nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(edges) > *input.Last {
-//			// Slice result to be of length last by removing edges from the start
-//			edges = edges[len(edges)-*input.Last:]
-//			hasPreviousPage = true
-//		}
-//	}
-//
-//	pageInfo := PageInfo{
-//		HasNextPage:     hasNextPage,
-//		HasPreviousPage: hasPreviousPage,
-//	}
-//
-//	return edges, pageInfo, nil
-//}
-
-//func EdgesToReturn(allEdges []Edge, before *cursor, after *cursor, first *int, last *int) ([]Edge, error) {
-//	result := ApplyCursorToEdges(allEdges, before, after)
-//
-//	if first != nil {
-//		if *first < 0 {
-//			return nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(result) > *first {
-//			// Slice result to be of length first by removing edges from the end
-//			result = result[:*first]
-//		}
-//	}
-//
-//	if last != nil {
-//		if *last < 0 {
-//			return nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(result) > *last {
-//			// Slice result to be of length last by removing edges from the start
-//			result = result[len(result)-*last:]
-//		}
-//	}
-//
-//	return result, nil
-//}
-
-//func ApplyCursorToEdges(allEdges []Edge, before *cursor, after *cursor) []Edge {
-//	result := allEdges
-//
-//	if after != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *after {
-//				// remove all previous element including the "after" one
-//				result = result[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if before != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *before {
-//				// remove all after element including the "before" one
-//				result = result[:i]
-//			}
-//		}
-//	}
-//
-//	return result
-//}
-
-//func HasPreviousPage(allEdges []Edge, before *cursor, after *cursor, last *int) bool {
-//	if last != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *last
-//	}
-//
-//	// TODO: handle "after", but according to the spec it's ok to return false
-//
-//	return false
-//}
-//
-//func HasNextPage(allEdges []Edge, before *cursor, after *cursor, first *int) bool {
-//	if first != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *first
-//	}
-//
-//	// TODO: handle "before", but according to the spec it's ok to return false
-//
-//	return false

graphql/resolvers/pager_comment.go 🔗

@@ -1,225 +0,0 @@
-// This file was automatically generated by genny.
-// Any changes will be lost if this file is regenerated.
-// see https://github.com/cheekybits/genny
-
-package resolvers
-
-import (
-	"fmt"
-
-	"github.com/MichaelMure/git-bug/bug"
-)
-
-type BugCommentEdger func(value bug.Comment, offset int) Edge
-
-func BugCommentPaginate(source []bug.Comment, edger BugCommentEdger, input ConnectionInput) ([]CommentEdge, PageInfo, error) {
-	var result []CommentEdge
-	var pageInfo PageInfo
-
-	offset := 0
-
-	if input.After != nil {
-		for i, value := range source {
-			edge := edger(value, i)
-			if edge.GetCursor() == *input.After {
-				// remove all previous element including the "after" one
-				source = source[i+1:]
-				offset = i + 1
-				break
-			}
-		}
-	}
-
-	if input.Before != nil {
-		for i, value := range source {
-			edge := edger(value, i+offset)
-
-			if edge.GetCursor() == *input.Before {
-				// remove all after element including the "before" one
-				break
-			}
-
-			result = append(result, edge.(CommentEdge))
-		}
-	} else {
-		result = make([]CommentEdge, len(source))
-
-		for i, value := range source {
-			result[i] = edger(value, i+offset).(CommentEdge)
-		}
-	}
-
-	if input.First != nil {
-		if *input.First < 0 {
-			return nil, PageInfo{}, fmt.Errorf("first less than zero")
-		}
-
-		if len(result) > *input.First {
-			// Slice result to be of length first by removing edges from the end
-			result = result[:*input.First]
-			pageInfo.HasNextPage = true
-		}
-	}
-
-	if input.Last != nil {
-		if *input.Last < 0 {
-			return nil, PageInfo{}, fmt.Errorf("last less than zero")
-		}
-
-		if len(result) > *input.Last {
-			// Slice result to be of length last by removing edges from the start
-			result = result[len(result)-*input.Last:]
-			pageInfo.HasPreviousPage = true
-		}
-	}
-
-	return result, pageInfo, nil
-}
-
-// Apply the before/after cursor params to the source and return an array of edges
-//func ApplyCursorToEdges(source []interface{}, edger Edger, input ConnectionInput) []Edge {
-//	var result []Edge
-//
-//	if input.After != nil {
-//		for i, value := range source {
-//			edge := edger(value)
-//			if edge.Cursor() == *input.After {
-//				// remove all previous element including the "after" one
-//				source = source[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if input.Before != nil {
-//		for _, value := range source {
-//			edge := edger(value)
-//
-//			if edge.Cursor() == *input.Before {
-//				// remove all after element including the "before" one
-//				break
-//			}
-//
-//			result = append(result, edge)
-//		}
-//	} else {
-//		result = make([]Edge, len(source))
-//
-//		for i, value := range source {
-//			result[i] = edger(value)
-//		}
-//	}
-//
-//	return result
-//}
-
-// Apply the first/last cursor params to the edges
-//func EdgesToReturn(edges []Edge, input ConnectionInput) ([]Edge, PageInfo, error) {
-//	hasPreviousPage := false
-//	hasNextPage := false
-//
-//	if input.First != nil {
-//		if *input.First < 0 {
-//			return nil, nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(edges) > *input.First {
-//			// Slice result to be of length first by removing edges from the end
-//			edges = edges[:*input.First]
-//			hasNextPage = true
-//		}
-//	}
-//
-//	if input.Last != nil {
-//		if *input.Last < 0 {
-//			return nil, nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(edges) > *input.Last {
-//			// Slice result to be of length last by removing edges from the start
-//			edges = edges[len(edges)-*input.Last:]
-//			hasPreviousPage = true
-//		}
-//	}
-//
-//	pageInfo := PageInfo{
-//		HasNextPage:     hasNextPage,
-//		HasPreviousPage: hasPreviousPage,
-//	}
-//
-//	return edges, pageInfo, nil
-//}
-
-//func EdgesToReturn(allEdges []Edge, before *cursor, after *cursor, first *int, last *int) ([]Edge, error) {
-//	result := ApplyCursorToEdges(allEdges, before, after)
-//
-//	if first != nil {
-//		if *first < 0 {
-//			return nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(result) > *first {
-//			// Slice result to be of length first by removing edges from the end
-//			result = result[:*first]
-//		}
-//	}
-//
-//	if last != nil {
-//		if *last < 0 {
-//			return nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(result) > *last {
-//			// Slice result to be of length last by removing edges from the start
-//			result = result[len(result)-*last:]
-//		}
-//	}
-//
-//	return result, nil
-//}
-
-//func ApplyCursorToEdges(allEdges []Edge, before *cursor, after *cursor) []Edge {
-//	result := allEdges
-//
-//	if after != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *after {
-//				// remove all previous element including the "after" one
-//				result = result[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if before != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *before {
-//				// remove all after element including the "before" one
-//				result = result[:i]
-//			}
-//		}
-//	}
-//
-//	return result
-//}
-
-//func HasPreviousPage(allEdges []Edge, before *cursor, after *cursor, last *int) bool {
-//	if last != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *last
-//	}
-//
-//	// TODO: handle "after", but according to the spec it's ok to return false
-//
-//	return false
-//}
-//
-//func HasNextPage(allEdges []Edge, before *cursor, after *cursor, first *int) bool {
-//	if first != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *first
-//	}
-//
-//	// TODO: handle "before", but according to the spec it's ok to return false
-//
-//	return false

graphql/resolvers/pager_operation.go 🔗

@@ -1,225 +0,0 @@
-// This file was automatically generated by genny.
-// Any changes will be lost if this file is regenerated.
-// see https://github.com/cheekybits/genny
-
-package resolvers
-
-import (
-	"fmt"
-
-	"github.com/MichaelMure/git-bug/bug"
-)
-
-type BugOperationEdger func(value bug.Operation, offset int) Edge
-
-func BugOperationPaginate(source []bug.Operation, edger BugOperationEdger, input ConnectionInput) ([]OperationEdge, PageInfo, error) {
-	var result []OperationEdge
-	var pageInfo PageInfo
-
-	offset := 0
-
-	if input.After != nil {
-		for i, value := range source {
-			edge := edger(value, i)
-			if edge.GetCursor() == *input.After {
-				// remove all previous element including the "after" one
-				source = source[i+1:]
-				offset = i + 1
-				break
-			}
-		}
-	}
-
-	if input.Before != nil {
-		for i, value := range source {
-			edge := edger(value, i+offset)
-
-			if edge.GetCursor() == *input.Before {
-				// remove all after element including the "before" one
-				break
-			}
-
-			result = append(result, edge.(OperationEdge))
-		}
-	} else {
-		result = make([]OperationEdge, len(source))
-
-		for i, value := range source {
-			result[i] = edger(value, i+offset).(OperationEdge)
-		}
-	}
-
-	if input.First != nil {
-		if *input.First < 0 {
-			return nil, PageInfo{}, fmt.Errorf("first less than zero")
-		}
-
-		if len(result) > *input.First {
-			// Slice result to be of length first by removing edges from the end
-			result = result[:*input.First]
-			pageInfo.HasNextPage = true
-		}
-	}
-
-	if input.Last != nil {
-		if *input.Last < 0 {
-			return nil, PageInfo{}, fmt.Errorf("last less than zero")
-		}
-
-		if len(result) > *input.Last {
-			// Slice result to be of length last by removing edges from the start
-			result = result[len(result)-*input.Last:]
-			pageInfo.HasPreviousPage = true
-		}
-	}
-
-	return result, pageInfo, nil
-}
-
-// Apply the before/after cursor params to the source and return an array of edges
-//func ApplyCursorToEdges(source []interface{}, edger Edger, input ConnectionInput) []Edge {
-//	var result []Edge
-//
-//	if input.After != nil {
-//		for i, value := range source {
-//			edge := edger(value)
-//			if edge.Cursor() == *input.After {
-//				// remove all previous element including the "after" one
-//				source = source[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if input.Before != nil {
-//		for _, value := range source {
-//			edge := edger(value)
-//
-//			if edge.Cursor() == *input.Before {
-//				// remove all after element including the "before" one
-//				break
-//			}
-//
-//			result = append(result, edge)
-//		}
-//	} else {
-//		result = make([]Edge, len(source))
-//
-//		for i, value := range source {
-//			result[i] = edger(value)
-//		}
-//	}
-//
-//	return result
-//}
-
-// Apply the first/last cursor params to the edges
-//func EdgesToReturn(edges []Edge, input ConnectionInput) ([]Edge, PageInfo, error) {
-//	hasPreviousPage := false
-//	hasNextPage := false
-//
-//	if input.First != nil {
-//		if *input.First < 0 {
-//			return nil, nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(edges) > *input.First {
-//			// Slice result to be of length first by removing edges from the end
-//			edges = edges[:*input.First]
-//			hasNextPage = true
-//		}
-//	}
-//
-//	if input.Last != nil {
-//		if *input.Last < 0 {
-//			return nil, nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(edges) > *input.Last {
-//			// Slice result to be of length last by removing edges from the start
-//			edges = edges[len(edges)-*input.Last:]
-//			hasPreviousPage = true
-//		}
-//	}
-//
-//	pageInfo := PageInfo{
-//		HasNextPage:     hasNextPage,
-//		HasPreviousPage: hasPreviousPage,
-//	}
-//
-//	return edges, pageInfo, nil
-//}
-
-//func EdgesToReturn(allEdges []Edge, before *cursor, after *cursor, first *int, last *int) ([]Edge, error) {
-//	result := ApplyCursorToEdges(allEdges, before, after)
-//
-//	if first != nil {
-//		if *first < 0 {
-//			return nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(result) > *first {
-//			// Slice result to be of length first by removing edges from the end
-//			result = result[:*first]
-//		}
-//	}
-//
-//	if last != nil {
-//		if *last < 0 {
-//			return nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(result) > *last {
-//			// Slice result to be of length last by removing edges from the start
-//			result = result[len(result)-*last:]
-//		}
-//	}
-//
-//	return result, nil
-//}
-
-//func ApplyCursorToEdges(allEdges []Edge, before *cursor, after *cursor) []Edge {
-//	result := allEdges
-//
-//	if after != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *after {
-//				// remove all previous element including the "after" one
-//				result = result[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if before != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *before {
-//				// remove all after element including the "before" one
-//				result = result[:i]
-//			}
-//		}
-//	}
-//
-//	return result
-//}
-
-//func HasPreviousPage(allEdges []Edge, before *cursor, after *cursor, last *int) bool {
-//	if last != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *last
-//	}
-//
-//	// TODO: handle "after", but according to the spec it's ok to return false
-//
-//	return false
-//}
-//
-//func HasNextPage(allEdges []Edge, before *cursor, after *cursor, first *int) bool {
-//	if first != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *first
-//	}
-//
-//	// TODO: handle "before", but according to the spec it's ok to return false
-//
-//	return false

graphql/resolvers/pagers.go 🔗

@@ -1,51 +0,0 @@
-//go:generate genny -in=pagers_template.go -out=pager_bug.go gen "NodeType=bug.Snapshot EdgeType=BugEdge"
-//go:generate genny -in=pagers_template.go -out=pager_operation.go gen "NodeType=bug.Operation EdgeType=OperationEdge"
-//go:generate genny -in=pagers_template.go -out=pager_comment.go gen "NodeType=bug.Comment EdgeType=CommentEdge"
-
-package resolvers
-
-import (
-	"encoding/base64"
-	"fmt"
-	"strconv"
-	"strings"
-)
-
-const cursorPrefix = "cursor:"
-
-type Edge interface {
-	GetCursor() string
-}
-
-// Creates the cursor string from an offset
-func offsetToCursor(offset int) string {
-	str := fmt.Sprintf("%v%v", cursorPrefix, offset)
-	return base64.StdEncoding.EncodeToString([]byte(str))
-}
-
-// Re-derives the offset from the cursor string.
-func cursorToOffset(cursor string) (int, error) {
-	str := ""
-	b, err := base64.StdEncoding.DecodeString(cursor)
-	if err == nil {
-		str = string(b)
-	}
-	str = strings.Replace(str, cursorPrefix, "", -1)
-	offset, err := strconv.Atoi(str)
-	if err != nil {
-		return 0, fmt.Errorf("Invalid cursor")
-	}
-	return offset, nil
-}
-
-func (e OperationEdge) GetCursor() string {
-	return e.Cursor
-}
-
-func (e BugEdge) GetCursor() string {
-	return e.Cursor
-}
-
-func (e CommentEdge) GetCursor() string {
-	return e.Cursor
-}

graphql/resolvers/pagers_template.go 🔗

@@ -1,224 +0,0 @@
-package resolvers
-
-import (
-	"fmt"
-	"github.com/cheekybits/genny/generic"
-)
-
-type NodeType generic.Type
-type EdgeType generic.Type
-
-type NodeTypeEdger func(value NodeType, offset int) Edge
-
-func NodeTypePaginate(source []NodeType, edger NodeTypeEdger, input ConnectionInput) ([]EdgeType, PageInfo, error) {
-	var result []EdgeType
-	var pageInfo PageInfo
-
-	offset := 0
-
-	if input.After != nil {
-		for i, value := range source {
-			edge := edger(value, i)
-			if edge.GetCursor() == *input.After {
-				// remove all previous element including the "after" one
-				source = source[i+1:]
-				offset = i + 1
-				break
-			}
-		}
-	}
-
-	if input.Before != nil {
-		for i, value := range source {
-			edge := edger(value, i+offset)
-
-			if edge.GetCursor() == *input.Before {
-				// remove all after element including the "before" one
-				break
-			}
-
-			result = append(result, edge.(EdgeType))
-		}
-	} else {
-		result = make([]EdgeType, len(source))
-
-		for i, value := range source {
-			result[i] = edger(value, i+offset).(EdgeType)
-		}
-	}
-
-	if input.First != nil {
-		if *input.First < 0 {
-			return nil, PageInfo{}, fmt.Errorf("first less than zero")
-		}
-
-		if len(result) > *input.First {
-			// Slice result to be of length first by removing edges from the end
-			result = result[:*input.First]
-			pageInfo.HasNextPage = true
-		}
-	}
-
-	if input.Last != nil {
-		if *input.Last < 0 {
-			return nil, PageInfo{}, fmt.Errorf("last less than zero")
-		}
-
-		if len(result) > *input.Last {
-			// Slice result to be of length last by removing edges from the start
-			result = result[len(result)-*input.Last:]
-			pageInfo.HasPreviousPage = true
-		}
-	}
-
-	return result, pageInfo, nil
-}
-
-// Apply the before/after cursor params to the source and return an array of edges
-//func ApplyCursorToEdges(source []interface{}, edger Edger, input ConnectionInput) []Edge {
-//	var result []Edge
-//
-//	if input.After != nil {
-//		for i, value := range source {
-//			edge := edger(value)
-//			if edge.Cursor() == *input.After {
-//				// remove all previous element including the "after" one
-//				source = source[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if input.Before != nil {
-//		for _, value := range source {
-//			edge := edger(value)
-//
-//			if edge.Cursor() == *input.Before {
-//				// remove all after element including the "before" one
-//				break
-//			}
-//
-//			result = append(result, edge)
-//		}
-//	} else {
-//		result = make([]Edge, len(source))
-//
-//		for i, value := range source {
-//			result[i] = edger(value)
-//		}
-//	}
-//
-//	return result
-//}
-
-// Apply the first/last cursor params to the edges
-//func EdgesToReturn(edges []Edge, input ConnectionInput) ([]Edge, PageInfo, error) {
-//	hasPreviousPage := false
-//	hasNextPage := false
-//
-//	if input.First != nil {
-//		if *input.First < 0 {
-//			return nil, nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(edges) > *input.First {
-//			// Slice result to be of length first by removing edges from the end
-//			edges = edges[:*input.First]
-//			hasNextPage = true
-//		}
-//	}
-//
-//	if input.Last != nil {
-//		if *input.Last < 0 {
-//			return nil, nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(edges) > *input.Last {
-//			// Slice result to be of length last by removing edges from the start
-//			edges = edges[len(edges)-*input.Last:]
-//			hasPreviousPage = true
-//		}
-//	}
-//
-//	pageInfo := PageInfo{
-//		HasNextPage:     hasNextPage,
-//		HasPreviousPage: hasPreviousPage,
-//	}
-//
-//	return edges, pageInfo, nil
-//}
-
-//func EdgesToReturn(allEdges []Edge, before *cursor, after *cursor, first *int, last *int) ([]Edge, error) {
-//	result := ApplyCursorToEdges(allEdges, before, after)
-//
-//	if first != nil {
-//		if *first < 0 {
-//			return nil, fmt.Errorf("first less than zero")
-//		}
-//
-//		if len(result) > *first {
-//			// Slice result to be of length first by removing edges from the end
-//			result = result[:*first]
-//		}
-//	}
-//
-//	if last != nil {
-//		if *last < 0 {
-//			return nil, fmt.Errorf("last less than zero")
-//		}
-//
-//		if len(result) > *last {
-//			// Slice result to be of length last by removing edges from the start
-//			result = result[len(result)-*last:]
-//		}
-//	}
-//
-//	return result, nil
-//}
-
-//func ApplyCursorToEdges(allEdges []Edge, before *cursor, after *cursor) []Edge {
-//	result := allEdges
-//
-//	if after != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *after {
-//				// remove all previous element including the "after" one
-//				result = result[i+1:]
-//				break
-//			}
-//		}
-//	}
-//
-//	if before != nil {
-//		for i, edge := range result {
-//			if edge.Cursor() == *before {
-//				// remove all after element including the "before" one
-//				result = result[:i]
-//			}
-//		}
-//	}
-//
-//	return result
-//}
-
-//func HasPreviousPage(allEdges []Edge, before *cursor, after *cursor, last *int) bool {
-//	if last != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *last
-//	}
-//
-//	// TODO: handle "after", but according to the spec it's ok to return false
-//
-//	return false
-//}
-//
-//func HasNextPage(allEdges []Edge, before *cursor, after *cursor, first *int) bool {
-//	if first != nil {
-//		edges := ApplyCursorToEdges(allEdges, before, after)
-//		return len(edges) > *first
-//	}
-//
-//	// TODO: handle "before", but according to the spec it's ok to return false
-//
-//	return false
-//}

graphql/resolvers/repo.go 🔗

@@ -4,6 +4,7 @@ import (
 	"context"
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/cache"
+	"github.com/MichaelMure/git-bug/graphql/models"
 )
 
 type repoResolver struct {
@@ -11,7 +12,7 @@ type repoResolver struct {
 	repo  cache.RepoCacher
 }
 
-func (repoResolver) AllBugs(ctx context.Context, obj *repoResolver, input ConnectionInput) (BugConnection, error) {
+func (repoResolver) AllBugs(ctx context.Context, obj *repoResolver, input models.ConnectionInput) (models.BugConnection, error) {
 	panic("implement me")
 }
 

graphql/schema.graphql 🔗

@@ -138,7 +138,7 @@ union OperationUnion =
 # The connection type for Bug.
 type BugConnection {
   # A list of edges.
-  edges: [BugEdge]!
+  edges: [BugEdge!]!
 
   # Information to aid in pagination.
   pageInfo: PageInfo!