graphql: add board in the repo

Michael Muré created

Change summary

api/graphql/connections/edges.go          |  11 
api/graphql/graph/board.generated.go      |  21 +
api/graphql/graph/repository.generated.go | 356 +++++++++++++++++++++++++
api/graphql/graph/root.generated.go       |   4 
api/graphql/graph/root_.generated.go      |  42 ++
api/graphql/resolvers/repo.go             |  59 ++++
api/graphql/schema/repository.graphql     |  16 +
7 files changed, 509 insertions(+)

Detailed changes

api/graphql/connections/edges.go 🔗

@@ -2,6 +2,17 @@ package connections
 
 import "github.com/git-bug/git-bug/entity"
 
+// LazyBoardEdge is a special relay edge used to implement a lazy loading connection
+type LazyBoardEdge struct {
+	Id     entity.Id
+	Cursor string
+}
+
+// GetCursor return the cursor of a LazyBoardEdge
+func (lbe LazyBoardEdge) GetCursor() string {
+	return lbe.Cursor
+}
+
 // LazyBugEdge is a special relay edge used to implement a lazy loading connection
 type LazyBugEdge struct {
 	Id     entity.Id

api/graphql/graph/board.generated.go 🔗

@@ -1480,6 +1480,20 @@ func (ec *executionContext) marshalNBoard2ᚕgithubᚗcomᚋgitᚑbugᚋgitᚑbu
 	return ret
 }
 
+func (ec *executionContext) marshalNBoardConnection2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBoardConnection(ctx context.Context, sel ast.SelectionSet, v models.BoardConnection) graphql.Marshaler {
+	return ec._BoardConnection(ctx, sel, &v)
+}
+
+func (ec *executionContext) marshalNBoardConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBoardConnection(ctx context.Context, sel ast.SelectionSet, v *models.BoardConnection) graphql.Marshaler {
+	if v == nil {
+		if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+			ec.Errorf(ctx, "the requested element is null which the schema does not allow")
+		}
+		return graphql.Null
+	}
+	return ec._BoardConnection(ctx, sel, v)
+}
+
 func (ec *executionContext) marshalNBoardEdge2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBoardEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.BoardEdge) graphql.Marshaler {
 	ret := make(graphql.Array, len(v))
 	var wg sync.WaitGroup
@@ -1534,4 +1548,11 @@ func (ec *executionContext) marshalNBoardEdge2ᚖgithubᚗcomᚋgitᚑbugᚋgit
 	return ec._BoardEdge(ctx, sel, v)
 }
 
+func (ec *executionContext) marshalOBoard2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBoardWrapper(ctx context.Context, sel ast.SelectionSet, v models.BoardWrapper) graphql.Marshaler {
+	if v == nil {
+		return graphql.Null
+	}
+	return ec._Board(ctx, sel, v)
+}
+
 // endregion ***************************** type.gotpl *****************************

api/graphql/graph/repository.generated.go 🔗

@@ -18,6 +18,8 @@ import (
 
 type RepositoryResolver interface {
 	Name(ctx context.Context, obj *models.Repository) (*string, error)
+	AllBoards(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BoardConnection, error)
+	Board(ctx context.Context, obj *models.Repository, prefix string) (models.BoardWrapper, error)
 	AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BugConnection, error)
 	Bug(ctx context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error)
 	AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
@@ -30,6 +32,126 @@ type RepositoryResolver interface {
 
 // region    ***************************** args.gotpl *****************************
 
+func (ec *executionContext) field_Repository_allBoards_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
+	var err error
+	args := map[string]any{}
+	arg0, err := ec.field_Repository_allBoards_argsAfter(ctx, rawArgs)
+	if err != nil {
+		return nil, err
+	}
+	args["after"] = arg0
+	arg1, err := ec.field_Repository_allBoards_argsBefore(ctx, rawArgs)
+	if err != nil {
+		return nil, err
+	}
+	args["before"] = arg1
+	arg2, err := ec.field_Repository_allBoards_argsFirst(ctx, rawArgs)
+	if err != nil {
+		return nil, err
+	}
+	args["first"] = arg2
+	arg3, err := ec.field_Repository_allBoards_argsLast(ctx, rawArgs)
+	if err != nil {
+		return nil, err
+	}
+	args["last"] = arg3
+	arg4, err := ec.field_Repository_allBoards_argsQuery(ctx, rawArgs)
+	if err != nil {
+		return nil, err
+	}
+	args["query"] = arg4
+	return args, nil
+}
+func (ec *executionContext) field_Repository_allBoards_argsAfter(
+	ctx context.Context,
+	rawArgs map[string]any,
+) (*string, error) {
+	if _, ok := rawArgs["after"]; !ok {
+		var zeroVal *string
+		return zeroVal, nil
+	}
+
+	ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after"))
+	if tmp, ok := rawArgs["after"]; ok {
+		return ec.unmarshalOString2ᚖstring(ctx, tmp)
+	}
+
+	var zeroVal *string
+	return zeroVal, nil
+}
+
+func (ec *executionContext) field_Repository_allBoards_argsBefore(
+	ctx context.Context,
+	rawArgs map[string]any,
+) (*string, error) {
+	if _, ok := rawArgs["before"]; !ok {
+		var zeroVal *string
+		return zeroVal, nil
+	}
+
+	ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before"))
+	if tmp, ok := rawArgs["before"]; ok {
+		return ec.unmarshalOString2ᚖstring(ctx, tmp)
+	}
+
+	var zeroVal *string
+	return zeroVal, nil
+}
+
+func (ec *executionContext) field_Repository_allBoards_argsFirst(
+	ctx context.Context,
+	rawArgs map[string]any,
+) (*int, error) {
+	if _, ok := rawArgs["first"]; !ok {
+		var zeroVal *int
+		return zeroVal, nil
+	}
+
+	ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first"))
+	if tmp, ok := rawArgs["first"]; ok {
+		return ec.unmarshalOInt2ᚖint(ctx, tmp)
+	}
+
+	var zeroVal *int
+	return zeroVal, nil
+}
+
+func (ec *executionContext) field_Repository_allBoards_argsLast(
+	ctx context.Context,
+	rawArgs map[string]any,
+) (*int, error) {
+	if _, ok := rawArgs["last"]; !ok {
+		var zeroVal *int
+		return zeroVal, nil
+	}
+
+	ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last"))
+	if tmp, ok := rawArgs["last"]; ok {
+		return ec.unmarshalOInt2ᚖint(ctx, tmp)
+	}
+
+	var zeroVal *int
+	return zeroVal, nil
+}
+
+func (ec *executionContext) field_Repository_allBoards_argsQuery(
+	ctx context.Context,
+	rawArgs map[string]any,
+) (*string, error) {
+	if _, ok := rawArgs["query"]; !ok {
+		var zeroVal *string
+		return zeroVal, nil
+	}
+
+	ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("query"))
+	if tmp, ok := rawArgs["query"]; ok {
+		return ec.unmarshalOString2ᚖstring(ctx, tmp)
+	}
+
+	var zeroVal *string
+	return zeroVal, nil
+}
+
 func (ec *executionContext) field_Repository_allBugs_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
 	var err error
 	args := map[string]any{}
@@ -247,6 +369,34 @@ func (ec *executionContext) field_Repository_allIdentities_argsLast(
 	return zeroVal, nil
 }
 
+func (ec *executionContext) field_Repository_board_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
+	var err error
+	args := map[string]any{}
+	arg0, err := ec.field_Repository_board_argsPrefix(ctx, rawArgs)
+	if err != nil {
+		return nil, err
+	}
+	args["prefix"] = arg0
+	return args, nil
+}
+func (ec *executionContext) field_Repository_board_argsPrefix(
+	ctx context.Context,
+	rawArgs map[string]any,
+) (string, error) {
+	if _, ok := rawArgs["prefix"]; !ok {
+		var zeroVal string
+		return zeroVal, nil
+	}
+
+	ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("prefix"))
+	if tmp, ok := rawArgs["prefix"]; ok {
+		return ec.unmarshalNString2string(ctx, tmp)
+	}
+
+	var zeroVal string
+	return zeroVal, nil
+}
+
 func (ec *executionContext) field_Repository_bug_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
 	var err error
 	args := map[string]any{}
@@ -449,6 +599,143 @@ func (ec *executionContext) fieldContext_Repository_name(_ context.Context, fiel
 	return fc, nil
 }
 
+func (ec *executionContext) _Repository_allBoards(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
+	fc, err := ec.fieldContext_Repository_allBoards(ctx, field)
+	if err != nil {
+		return graphql.Null
+	}
+	ctx = graphql.WithFieldContext(ctx, fc)
+	defer func() {
+		if r := recover(); r != nil {
+			ec.Error(ctx, ec.Recover(ctx, r))
+			ret = graphql.Null
+		}
+	}()
+	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+		ctx = rctx // use context from middleware stack in children
+		return ec.resolvers.Repository().AllBoards(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int), fc.Args["query"].(*string))
+	})
+	if err != nil {
+		ec.Error(ctx, err)
+		return graphql.Null
+	}
+	if resTmp == nil {
+		if !graphql.HasFieldError(ctx, fc) {
+			ec.Errorf(ctx, "must not be null")
+		}
+		return graphql.Null
+	}
+	res := resTmp.(*models.BoardConnection)
+	fc.Result = res
+	return ec.marshalNBoardConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBoardConnection(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Repository_allBoards(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+	fc = &graphql.FieldContext{
+		Object:     "Repository",
+		Field:      field,
+		IsMethod:   true,
+		IsResolver: true,
+		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+			switch field.Name {
+			case "edges":
+				return ec.fieldContext_BoardConnection_edges(ctx, field)
+			case "nodes":
+				return ec.fieldContext_BoardConnection_nodes(ctx, field)
+			case "pageInfo":
+				return ec.fieldContext_BoardConnection_pageInfo(ctx, field)
+			case "totalCount":
+				return ec.fieldContext_BoardConnection_totalCount(ctx, field)
+			}
+			return nil, fmt.Errorf("no field named %q was found under type BoardConnection", field.Name)
+		},
+	}
+	defer func() {
+		if r := recover(); r != nil {
+			err = ec.Recover(ctx, r)
+			ec.Error(ctx, err)
+		}
+	}()
+	ctx = graphql.WithFieldContext(ctx, fc)
+	if fc.Args, err = ec.field_Repository_allBoards_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+		ec.Error(ctx, err)
+		return fc, err
+	}
+	return fc, nil
+}
+
+func (ec *executionContext) _Repository_board(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
+	fc, err := ec.fieldContext_Repository_board(ctx, field)
+	if err != nil {
+		return graphql.Null
+	}
+	ctx = graphql.WithFieldContext(ctx, fc)
+	defer func() {
+		if r := recover(); r != nil {
+			ec.Error(ctx, ec.Recover(ctx, r))
+			ret = graphql.Null
+		}
+	}()
+	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+		ctx = rctx // use context from middleware stack in children
+		return ec.resolvers.Repository().Board(rctx, obj, fc.Args["prefix"].(string))
+	})
+	if err != nil {
+		ec.Error(ctx, err)
+		return graphql.Null
+	}
+	if resTmp == nil {
+		return graphql.Null
+	}
+	res := resTmp.(models.BoardWrapper)
+	fc.Result = res
+	return ec.marshalOBoard2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBoardWrapper(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Repository_board(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+	fc = &graphql.FieldContext{
+		Object:     "Repository",
+		Field:      field,
+		IsMethod:   true,
+		IsResolver: true,
+		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+			switch field.Name {
+			case "id":
+				return ec.fieldContext_Board_id(ctx, field)
+			case "humanId":
+				return ec.fieldContext_Board_humanId(ctx, field)
+			case "createdAt":
+				return ec.fieldContext_Board_createdAt(ctx, field)
+			case "lastEdit":
+				return ec.fieldContext_Board_lastEdit(ctx, field)
+			case "title":
+				return ec.fieldContext_Board_title(ctx, field)
+			case "description":
+				return ec.fieldContext_Board_description(ctx, field)
+			case "columns":
+				return ec.fieldContext_Board_columns(ctx, field)
+			case "actors":
+				return ec.fieldContext_Board_actors(ctx, field)
+			case "operations":
+				return ec.fieldContext_Board_operations(ctx, field)
+			}
+			return nil, fmt.Errorf("no field named %q was found under type Board", field.Name)
+		},
+	}
+	defer func() {
+		if r := recover(); r != nil {
+			err = ec.Recover(ctx, r)
+			ec.Error(ctx, err)
+		}
+	}()
+	ctx = graphql.WithFieldContext(ctx, fc)
+	if fc.Args, err = ec.field_Repository_board_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+		ec.Error(ctx, err)
+		return fc, err
+	}
+	return fc, nil
+}
+
 func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
 	fc, err := ec.fieldContext_Repository_allBugs(ctx, field)
 	if err != nil {
@@ -908,6 +1195,75 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe
 				continue
 			}
 
+			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+		case "allBoards":
+			field := field
+
+			innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._Repository_allBoards(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&fs.Invalids, 1)
+				}
+				return res
+			}
+
+			if field.Deferrable != nil {
+				dfs, ok := deferred[field.Deferrable.Label]
+				di := 0
+				if ok {
+					dfs.AddField(field)
+					di = len(dfs.Values) - 1
+				} else {
+					dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+					deferred[field.Deferrable.Label] = dfs
+				}
+				dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+					return innerFunc(ctx, dfs)
+				})
+
+				// don't run the out.Concurrently() call below
+				out.Values[i] = graphql.Null
+				continue
+			}
+
+			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+		case "board":
+			field := field
+
+			innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._Repository_board(ctx, field, obj)
+				return res
+			}
+
+			if field.Deferrable != nil {
+				dfs, ok := deferred[field.Deferrable.Label]
+				di := 0
+				if ok {
+					dfs.AddField(field)
+					di = len(dfs.Values) - 1
+				} else {
+					dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+					deferred[field.Deferrable.Label] = dfs
+				}
+				dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+					return innerFunc(ctx, dfs)
+				})
+
+				// don't run the out.Concurrently() call below
+				out.Values[i] = graphql.Null
+				continue
+			}
+
 			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
 		case "allBugs":
 			field := field

api/graphql/graph/root.generated.go 🔗

@@ -962,6 +962,10 @@ func (ec *executionContext) fieldContext_Query_repository(ctx context.Context, f
 			switch field.Name {
 			case "name":
 				return ec.fieldContext_Repository_name(ctx, field)
+			case "allBoards":
+				return ec.fieldContext_Repository_allBoards(ctx, field)
+			case "board":
+				return ec.fieldContext_Repository_board(ctx, field)
 			case "allBugs":
 				return ec.fieldContext_Repository_allBugs(ctx, field)
 			case "bug":

api/graphql/graph/root_.generated.go 🔗

@@ -482,8 +482,10 @@ type ComplexityRoot struct {
 	}
 
 	Repository struct {
+		AllBoards     func(childComplexity int, after *string, before *string, first *int, last *int, query *string) int
 		AllBugs       func(childComplexity int, after *string, before *string, first *int, last *int, query *string) int
 		AllIdentities func(childComplexity int, after *string, before *string, first *int, last *int) int
+		Board         func(childComplexity int, prefix string) int
 		Bug           func(childComplexity int, prefix string) int
 		Identity      func(childComplexity int, prefix string) int
 		Name          func(childComplexity int) int
@@ -2300,6 +2302,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
 
 		return e.complexity.Query.Repository(childComplexity, args["ref"].(*string)), true
 
+	case "Repository.allBoards":
+		if e.complexity.Repository.AllBoards == nil {
+			break
+		}
+
+		args, err := ec.field_Repository_allBoards_args(ctx, rawArgs)
+		if err != nil {
+			return 0, false
+		}
+
+		return e.complexity.Repository.AllBoards(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int), args["query"].(*string)), true
+
 	case "Repository.allBugs":
 		if e.complexity.Repository.AllBugs == nil {
 			break
@@ -2324,6 +2338,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
 
 		return e.complexity.Repository.AllIdentities(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true
 
+	case "Repository.board":
+		if e.complexity.Repository.Board == nil {
+			break
+		}
+
+		args, err := ec.field_Repository_board_args(ctx, rawArgs)
+		if err != nil {
+			return 0, false
+		}
+
+		return e.complexity.Repository.Board(childComplexity, args["prefix"].(string)), true
+
 	case "Repository.bug":
 		if e.complexity.Repository.Bug == nil {
 			break
@@ -3338,6 +3364,22 @@ type OperationEdge {
     """The name of the repository"""
     name: String
 
+    """All the boards"""
+    allBoards(
+      """Returns the elements in the list that come after the specified cursor."""
+      after: String
+      """Returns the elements in the list that come before the specified cursor."""
+      before: String
+      """Returns the first _n_ elements from the list."""
+      first: Int
+      """Returns the last _n_ elements from the list."""
+      last: Int
+      """A query to select and order bugs."""
+      query: String
+    ): BoardConnection!
+
+    board(prefix: String!): Board
+
     """All the bugs"""
     allBugs(
         """Returns the elements in the list that come after the specified cursor."""

api/graphql/resolvers/repo.go 🔗

@@ -21,6 +21,65 @@ func (repoResolver) Name(_ context.Context, obj *models.Repository) (*string, er
 	return &name, nil
 }
 
+func (r repoResolver) AllBoards(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BoardConnection, error) {
+	input := models.ConnectionInput{
+		Before: before,
+		After:  after,
+		First:  first,
+		Last:   last,
+	}
+
+	// Simply pass a []string with the ids to the pagination algorithm
+	source := obj.Repo.Boards().AllIds()
+
+	// The edger create a custom edge holding just the id
+	edger := func(id entity.Id, offset int) connections.Edge {
+		return connections.LazyBoardEdge{
+			Id:     id,
+			Cursor: connections.OffsetToCursor(offset),
+		}
+	}
+
+	// The conMaker will finally load and compile boards from git to replace the selected edges
+	conMaker := func(lazyBoardEdges []*connections.LazyBoardEdge, lazyNode []entity.Id, info *models.PageInfo, totalCount int) (*models.BoardConnection, error) {
+		edges := make([]*models.BoardEdge, len(lazyBoardEdges))
+		nodes := make([]models.BoardWrapper, len(lazyBoardEdges))
+
+		for k, lazyBoardEdge := range lazyBoardEdges {
+			excerpt, err := obj.Repo.Boards().ResolveExcerpt(lazyBoardEdge.Id)
+			if err != nil {
+				return nil, err
+			}
+
+			i := models.NewLazyBoard(obj.Repo, excerpt)
+
+			edges[k] = &models.BoardEdge{
+				Cursor: lazyBoardEdge.Cursor,
+				Node:   i,
+			}
+			nodes[k] = i
+		}
+
+		return &models.BoardConnection{
+			Edges:      edges,
+			Nodes:      nodes,
+			PageInfo:   info,
+			TotalCount: totalCount,
+		}, nil
+	}
+
+	return connections.Connection(source, edger, conMaker, input)
+}
+
+func (r repoResolver) Board(ctx context.Context, obj *models.Repository, prefix string) (models.BoardWrapper, error) {
+	excerpt, err := obj.Repo.Boards().ResolveExcerptPrefix(prefix)
+	if err != nil {
+		return nil, err
+	}
+
+	return models.NewLazyBoard(obj.Repo, excerpt), nil
+}
+
 func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,

api/graphql/schema/repository.graphql 🔗

@@ -2,6 +2,22 @@ type Repository {
     """The name of the repository"""
     name: String
 
+    """All the boards"""
+    allBoards(
+      """Returns the elements in the list that come after the specified cursor."""
+      after: String
+      """Returns the elements in the list that come before the specified cursor."""
+      before: String
+      """Returns the first _n_ elements from the list."""
+      first: Int
+      """Returns the last _n_ elements from the list."""
+      last: Int
+      """A query to select and order bugs."""
+      query: String
+    ): BoardConnection!
+
+    board(prefix: String!): Board
+
     """All the bugs"""
     allBugs(
         """Returns the elements in the list that come after the specified cursor."""