diff options
author | Michael Muré <batolettre@gmail.com> | 2018-09-29 20:58:25 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-09-29 20:59:25 +0200 |
commit | 36ebbe0cf4078070c3a6710f2e1f781c03de3d1a (patch) | |
tree | 1c739320485c55d2b68e766d6f15609c86a0eefe | |
parent | c46d01f8c10e6363b680fa6876e91bd8eaf3bb3e (diff) | |
download | git-bug-36ebbe0cf4078070c3a6710f2e1f781c03de3d1a.tar.gz |
graphql: expose the new Timeline
-rw-r--r-- | graphql/connections/connections.go | 1 | ||||
-rw-r--r-- | graphql/connections/gen_timeline.go | 111 | ||||
-rw-r--r-- | graphql/gqlgen.yml | 6 | ||||
-rw-r--r-- | graphql/graph/gen_graph.go | 981 | ||||
-rw-r--r-- | graphql/models/edges.go | 5 | ||||
-rw-r--r-- | graphql/models/gen_models.go | 12 | ||||
-rw-r--r-- | graphql/resolvers/bug.go | 27 | ||||
-rw-r--r-- | graphql/schema.graphql | 49 |
8 files changed, 1173 insertions, 19 deletions
diff --git a/graphql/connections/connections.go b/graphql/connections/connections.go index de5b8f1a..68608116 100644 --- a/graphql/connections/connections.go +++ b/graphql/connections/connections.go @@ -1,6 +1,7 @@ //go:generate genny -in=connection_template.go -out=gen_bug.go gen "NodeType=string EdgeType=LazyBugEdge 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" +//go:generate genny -in=connection_template.go -out=gen_timeline.go gen "NodeType=bug.TimelineItem EdgeType=models.TimelineItemEdge ConnectionType=models.TimelineItemConnection" // Package connections implement a generic GraphQL relay connection package connections diff --git a/graphql/connections/gen_timeline.go b/graphql/connections/gen_timeline.go new file mode 100644 index 00000000..4d417a45 --- /dev/null +++ b/graphql/connections/gen_timeline.go @@ -0,0 +1,111 @@ +// 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" +) + +// BugTimelineItemEdgeMaker define a function that take a bug.TimelineItem and an offset and +// create an Edge. +type BugTimelineItemEdgeMaker func(value bug.TimelineItem, offset int) Edge + +// BugTimelineItemConMaker define a function that create a models.TimelineItemConnection +type BugTimelineItemConMaker func( + edges []models.TimelineItemEdge, + nodes []bug.TimelineItem, + info models.PageInfo, + totalCount int) (models.TimelineItemConnection, error) + +// BugTimelineItemCon will paginate a source according to the input of a relay connection +func BugTimelineItemCon(source []bug.TimelineItem, edgeMaker BugTimelineItemEdgeMaker, conMaker BugTimelineItemConMaker, input models.ConnectionInput) (models.TimelineItemConnection, error) { + var nodes []bug.TimelineItem + var edges []models.TimelineItemEdge + var cursors []string + var pageInfo models.PageInfo + var totalCount = len(source) + + emptyCon, _ := conMaker(edges, nodes, pageInfo, 0) + + offset := 0 + + if input.After != nil { + for i, value := range source { + edge := edgeMaker(value, i) + if edge.GetCursor() == *input.After { + // remove all previous element including the "after" one + source = source[i+1:] + offset = i + 1 + pageInfo.HasPreviousPage = true + break + } + } + } + + if input.Before != nil { + for i, value := range source { + edge := edgeMaker(value, i+offset) + + if edge.GetCursor() == *input.Before { + // remove all after element including the "before" one + pageInfo.HasNextPage = true + break + } + + edges = append(edges, edge.(models.TimelineItemEdge)) + cursors = append(cursors, edge.GetCursor()) + nodes = append(nodes, value) + } + } else { + edges = make([]models.TimelineItemEdge, len(source)) + cursors = make([]string, len(source)) + nodes = source + + for i, value := range source { + edge := edgeMaker(value, i+offset) + edges[i] = edge.(models.TimelineItemEdge) + cursors[i] = edge.GetCursor() + } + } + + 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] + cursors = cursors[:*input.First] + nodes = nodes[:*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:] + cursors = cursors[len(cursors)-*input.Last:] + nodes = nodes[len(nodes)-*input.Last:] + pageInfo.HasPreviousPage = true + } + } + + // Fill up pageInfo cursors + if len(cursors) > 0 { + pageInfo.StartCursor = cursors[0] + pageInfo.EndCursor = cursors[len(cursors)-1] + } + + return conMaker(edges, nodes, pageInfo, totalCount) +} diff --git a/graphql/gqlgen.yml b/graphql/gqlgen.yml index 516f335f..4ff24f4e 100644 --- a/graphql/gqlgen.yml +++ b/graphql/gqlgen.yml @@ -31,3 +31,9 @@ models: model: github.com/MichaelMure/git-bug/bug.SetStatusOperation LabelChangeOperation: model: github.com/MichaelMure/git-bug/bug.LabelChangeOperation + TimelineItem: + model: github.com/MichaelMure/git-bug/bug.TimelineItem + CreateTimelineItem: + model: github.com/MichaelMure/git-bug/bug.CreateTimelineItem + CommentTimelineItem: + model: github.com/MichaelMure/git-bug/bug.CommentTimelineItem
\ No newline at end of file diff --git a/graphql/graph/gen_graph.go b/graphql/graph/gen_graph.go index 1425032a..032cf5cb 100644 --- a/graphql/graph/gen_graph.go +++ b/graphql/graph/gen_graph.go @@ -67,6 +67,7 @@ type ComplexityRoot struct { CreatedAt func(childComplexity int) int LastEdit func(childComplexity int) int Comments func(childComplexity int, after *string, before *string, first *int, last *int) int + Timeline func(childComplexity int, after *string, before *string, first *int, last *int) int Operations func(childComplexity int, after *string, before *string, first *int, last *int) int } @@ -100,6 +101,12 @@ type ComplexityRoot struct { Node func(childComplexity int) int } + CommentTimelineItem struct { + Hash func(childComplexity int) int + LastState func(childComplexity int) int + History func(childComplexity int) int + } + CreateOperation struct { Author func(childComplexity int) int Date func(childComplexity int) int @@ -108,7 +115,14 @@ type ComplexityRoot struct { Files func(childComplexity int) int } + CreateTimelineItem struct { + Hash func(childComplexity int) int + LastState func(childComplexity int) int + History func(childComplexity int) int + } + LabelChangeOperation struct { + Hash func(childComplexity int) int Author func(childComplexity int) int Date func(childComplexity int) int Added func(childComplexity int) int @@ -161,17 +175,31 @@ type ComplexityRoot struct { } SetStatusOperation struct { + Hash func(childComplexity int) int Author func(childComplexity int) int Date func(childComplexity int) int Status func(childComplexity int) int } SetTitleOperation struct { + Hash func(childComplexity int) int Author func(childComplexity int) int Date func(childComplexity int) int Title func(childComplexity int) int Was func(childComplexity int) int } + + TimelineItemConnection struct { + Edges func(childComplexity int) int + Nodes func(childComplexity int) int + PageInfo func(childComplexity int) int + TotalCount func(childComplexity int) int + } + + TimelineItemEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } } type AddCommentOperationResolver interface { @@ -183,6 +211,7 @@ type BugResolver interface { LastEdit(ctx context.Context, obj *bug.Snapshot) (time.Time, error) Comments(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (models.CommentConnection, error) + Timeline(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (models.TimelineItemConnection, error) Operations(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (models.OperationConnection, error) } type CreateOperationResolver interface { @@ -282,6 +311,68 @@ func field_Bug_comments_args(rawArgs map[string]interface{}) (map[string]interfa } +func field_Bug_timeline_args(rawArgs map[string]interface{}) (map[string]interface{}, error) { + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["after"]; ok { + var err error + var ptr1 string + if tmp != nil { + ptr1, err = graphql.UnmarshalString(tmp) + arg0 = &ptr1 + } + + if err != nil { + return nil, err + } + } + args["after"] = arg0 + var arg1 *string + if tmp, ok := rawArgs["before"]; ok { + var err error + var ptr1 string + if tmp != nil { + ptr1, err = graphql.UnmarshalString(tmp) + arg1 = &ptr1 + } + + if err != nil { + return nil, err + } + } + args["before"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["first"]; ok { + var err error + var ptr1 int + if tmp != nil { + ptr1, err = graphql.UnmarshalInt(tmp) + arg2 = &ptr1 + } + + if err != nil { + return nil, err + } + } + args["first"] = arg2 + var arg3 *int + if tmp, ok := rawArgs["last"]; ok { + var err error + var ptr1 int + if tmp != nil { + ptr1, err = graphql.UnmarshalInt(tmp) + arg3 = &ptr1 + } + + if err != nil { + return nil, err + } + } + args["last"] = arg3 + return args, nil + +} + func field_Bug_operations_args(rawArgs map[string]interface{}) (map[string]interface{}, error) { args := map[string]interface{}{} var arg0 *string @@ -914,6 +1005,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Bug.Comments(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true + case "Bug.timeline": + if e.complexity.Bug.Timeline == nil { + break + } + + args, err := field_Bug_timeline_args(rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Bug.Timeline(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true + case "Bug.operations": if e.complexity.Bug.Operations == nil { break @@ -1031,6 +1134,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommentEdge.Node(childComplexity), true + case "CommentTimelineItem.hash": + if e.complexity.CommentTimelineItem.Hash == nil { + break + } + + return e.complexity.CommentTimelineItem.Hash(childComplexity), true + + case "CommentTimelineItem.lastState": + if e.complexity.CommentTimelineItem.LastState == nil { + break + } + + return e.complexity.CommentTimelineItem.LastState(childComplexity), true + + case "CommentTimelineItem.history": + if e.complexity.CommentTimelineItem.History == nil { + break + } + + return e.complexity.CommentTimelineItem.History(childComplexity), true + case "CreateOperation.author": if e.complexity.CreateOperation.Author == nil { break @@ -1066,6 +1190,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CreateOperation.Files(childComplexity), true + case "CreateTimelineItem.hash": + if e.complexity.CreateTimelineItem.Hash == nil { + break + } + + return e.complexity.CreateTimelineItem.Hash(childComplexity), true + + case "CreateTimelineItem.lastState": + if e.complexity.CreateTimelineItem.LastState == nil { + break + } + + return e.complexity.CreateTimelineItem.LastState(childComplexity), true + + case "CreateTimelineItem.history": + if e.complexity.CreateTimelineItem.History == nil { + break + } + + return e.complexity.CreateTimelineItem.History(childComplexity), true + + case "LabelChangeOperation.hash": + if e.complexity.LabelChangeOperation.Hash == nil { + break + } + + return e.complexity.LabelChangeOperation.Hash(childComplexity), true + case "LabelChangeOperation.author": if e.complexity.LabelChangeOperation.Author == nil { break @@ -1312,6 +1464,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Repository.Bug(childComplexity, args["prefix"].(string)), true + case "SetStatusOperation.hash": + if e.complexity.SetStatusOperation.Hash == nil { + break + } + + return e.complexity.SetStatusOperation.Hash(childComplexity), true + case "SetStatusOperation.author": if e.complexity.SetStatusOperation.Author == nil { break @@ -1333,6 +1492,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SetStatusOperation.Status(childComplexity), true + case "SetTitleOperation.hash": + if e.complexity.SetTitleOperation.Hash == nil { + break + } + + return e.complexity.SetTitleOperation.Hash(childComplexity), true + case "SetTitleOperation.author": if e.complexity.SetTitleOperation.Author == nil { break @@ -1361,6 +1527,48 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SetTitleOperation.Was(childComplexity), true + case "TimelineItemConnection.edges": + if e.complexity.TimelineItemConnection.Edges == nil { + break + } + + return e.complexity.TimelineItemConnection.Edges(childComplexity), true + + case "TimelineItemConnection.nodes": + if e.complexity.TimelineItemConnection.Nodes == nil { + break + } + + return e.complexity.TimelineItemConnection.Nodes(childComplexity), true + + case "TimelineItemConnection.pageInfo": + if e.complexity.TimelineItemConnection.PageInfo == nil { + break + } + + return e.complexity.TimelineItemConnection.PageInfo(childComplexity), true + + case "TimelineItemConnection.totalCount": + if e.complexity.TimelineItemConnection.TotalCount == nil { + break + } + + return e.complexity.TimelineItemConnection.TotalCount(childComplexity), true + + case "TimelineItemEdge.cursor": + if e.complexity.TimelineItemEdge.Cursor == nil { + break + } + + return e.complexity.TimelineItemEdge.Cursor(childComplexity), true + + case "TimelineItemEdge.node": + if e.complexity.TimelineItemEdge.Node == nil { + break + } + + return e.complexity.TimelineItemEdge.Node(childComplexity), true + } return 0, false } @@ -1630,6 +1838,15 @@ func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj } wg.Done() }(i, field) + case "timeline": + wg.Add(1) + go func(i int, field graphql.CollectedField) { + out.Values[i] = ec._Bug_timeline(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + wg.Done() + }(i, field) case "operations": wg.Add(1) go func(i int, field graphql.CollectedField) { @@ -1866,6 +2083,35 @@ func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.Col } // nolint: vetshadow +func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) graphql.Marshaler { + rawArgs := field.ArgumentMap(ec.Variables) + args, err := field_Bug_timeline_args(rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx := &graphql.ResolverContext{ + Object: "Bug", + Args: args, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return ec.resolvers.Bug().Timeline(ctx, obj, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.TimelineItemConnection) + rctx.Result = res + + return ec._TimelineItemConnection(ctx, field.Selections, &res) +} + +// nolint: vetshadow func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) graphql.Marshaler { rawArgs := field.ArgumentMap(ec.Variables) args, err := field_Bug_operations_args(rawArgs) @@ -2570,6 +2816,146 @@ func (ec *executionContext) _CommentEdge_node(ctx context.Context, field graphql return ec._Comment(ctx, field.Selections, &res) } +var commentTimelineItemImplementors = []string{"CommentTimelineItem", "TimelineItem"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _CommentTimelineItem(ctx context.Context, sel ast.SelectionSet, obj *bug.CommentTimelineItem) graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, commentTimelineItemImplementors) + + out := graphql.NewOrderedMap(len(fields)) + invalid := false + for i, field := range fields { + out.Keys[i] = field.Alias + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CommentTimelineItem") + case "hash": + out.Values[i] = ec._CommentTimelineItem_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "lastState": + out.Values[i] = ec._CommentTimelineItem_lastState(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "history": + out.Values[i] = ec._CommentTimelineItem_history(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + if invalid { + return graphql.Null + } + return out +} + +// nolint: vetshadow +func (ec *executionContext) _CommentTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.CommentTimelineItem) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "CommentTimelineItem", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Hash() + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(git.Hash) + rctx.Result = res + return res +} + +// nolint: vetshadow +func (ec *executionContext) _CommentTimelineItem_lastState(ctx context.Context, field graphql.CollectedField, obj *bug.CommentTimelineItem) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "CommentTimelineItem", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.LastState(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bug.Comment) + rctx.Result = res + + return ec._Comment(ctx, field.Selections, &res) +} + +// nolint: vetshadow +func (ec *executionContext) _CommentTimelineItem_history(ctx context.Context, field graphql.CollectedField, obj *bug.CommentTimelineItem) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "CommentTimelineItem", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.History, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]bug.Comment) + rctx.Result = res + + arr1 := make(graphql.Array, len(res)) + var wg sync.WaitGroup + + isLen1 := len(res) == 1 + if !isLen1 { + wg.Add(len(res)) + } + + for idx1 := range res { + idx1 := idx1 + rctx := &graphql.ResolverContext{ + Index: &idx1, + Result: &res[idx1], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(idx1 int) { + if !isLen1 { + defer wg.Done() + } + arr1[idx1] = func() graphql.Marshaler { + + return ec._Comment(ctx, field.Selections, &res[idx1]) + }() + } + if isLen1 { + f(idx1) + } else { + go f(idx1) + } + + } + wg.Wait() + return arr1 +} + var createOperationImplementors = []string{"CreateOperation", "Operation", "Authored"} // nolint: gocyclo, errcheck, gas, goconst @@ -2749,7 +3135,147 @@ func (ec *executionContext) _CreateOperation_files(ctx context.Context, field gr return arr1 } -var labelChangeOperationImplementors = []string{"LabelChangeOperation", "Operation", "Authored"} +var createTimelineItemImplementors = []string{"CreateTimelineItem", "TimelineItem"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _CreateTimelineItem(ctx context.Context, sel ast.SelectionSet, obj *bug.CreateTimelineItem) graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, createTimelineItemImplementors) + + out := graphql.NewOrderedMap(len(fields)) + invalid := false + for i, field := range fields { + out.Keys[i] = field.Alias + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CreateTimelineItem") + case "hash": + out.Values[i] = ec._CreateTimelineItem_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "lastState": + out.Values[i] = ec._CreateTimelineItem_lastState(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "history": + out.Values[i] = ec._CreateTimelineItem_history(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + if invalid { + return graphql.Null + } + return out +} + +// nolint: vetshadow +func (ec *executionContext) _CreateTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "CreateTimelineItem", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Hash() + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(git.Hash) + rctx.Result = res + return res +} + +// nolint: vetshadow +func (ec *executionContext) _CreateTimelineItem_lastState(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "CreateTimelineItem", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.LastState(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bug.Comment) + rctx.Result = res + + return ec._Comment(ctx, field.Selections, &res) +} + +// nolint: vetshadow +func (ec *executionContext) _CreateTimelineItem_history(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "CreateTimelineItem", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.History, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]bug.Comment) + rctx.Result = res + + arr1 := make(graphql.Array, len(res)) + var wg sync.WaitGroup + + isLen1 := len(res) == 1 + if !isLen1 { + wg.Add(len(res)) + } + + for idx1 := range res { + idx1 := idx1 + rctx := &graphql.ResolverContext{ + Index: &idx1, + Result: &res[idx1], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(idx1 int) { + if !isLen1 { + defer wg.Done() + } + arr1[idx1] = func() graphql.Marshaler { + + return ec._Comment(ctx, field.Selections, &res[idx1]) + }() + } + if isLen1 { + f(idx1) + } else { + go f(idx1) + } + + } + wg.Wait() + return arr1 +} + +var labelChangeOperationImplementors = []string{"LabelChangeOperation", "Operation", "Authored", "TimelineItem"} // nolint: gocyclo, errcheck, gas, goconst func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.SelectionSet, obj *bug.LabelChangeOperation) graphql.Marshaler { @@ -2764,6 +3290,11 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("LabelChangeOperation") + case "hash": + out.Values[i] = ec._LabelChangeOperation_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } case "author": wg.Add(1) go func(i int, field graphql.CollectedField) { @@ -2804,6 +3335,28 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S } // nolint: vetshadow +func (ec *executionContext) _LabelChangeOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "LabelChangeOperation", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Hash() + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(git.Hash) + rctx.Result = res + return res +} + +// nolint: vetshadow func (ec *executionContext) _LabelChangeOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) graphql.Marshaler { rctx := &graphql.ResolverContext{ Object: "LabelChangeOperation", @@ -3938,7 +4491,7 @@ func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.C return ec._Bug(ctx, field.Selections, res) } -var setStatusOperationImplementors = []string{"SetStatusOperation", "Operation", "Authored"} +var setStatusOperationImplementors = []string{"SetStatusOperation", "Operation", "Authored", "TimelineItem"} // nolint: gocyclo, errcheck, gas, goconst func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.SelectionSet, obj *bug.SetStatusOperation) graphql.Marshaler { @@ -3953,6 +4506,11 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("SetStatusOperation") + case "hash": + out.Values[i] = ec._SetStatusOperation_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } case "author": wg.Add(1) go func(i int, field graphql.CollectedField) { @@ -3992,6 +4550,28 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel } // nolint: vetshadow +func (ec *executionContext) _SetStatusOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "SetStatusOperation", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Hash() + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(git.Hash) + rctx.Result = res + return res +} + +// nolint: vetshadow func (ec *executionContext) _SetStatusOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) graphql.Marshaler { rctx := &graphql.ResolverContext{ Object: "SetStatusOperation", @@ -4058,7 +4638,7 @@ func (ec *executionContext) _SetStatusOperation_status(ctx context.Context, fiel return res } -var setTitleOperationImplementors = []string{"SetTitleOperation", "Operation", "Authored"} +var setTitleOperationImplementors = []string{"SetTitleOperation", "Operation", "Authored", "TimelineItem"} // nolint: gocyclo, errcheck, gas, goconst func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.SelectionSet, obj *bug.SetTitleOperation) graphql.Marshaler { @@ -4073,6 +4653,11 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("SetTitleOperation") + case "hash": + out.Values[i] = ec._SetTitleOperation_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } case "author": wg.Add(1) go func(i int, field graphql.CollectedField) { @@ -4113,6 +4698,28 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele } // nolint: vetshadow +func (ec *executionContext) _SetTitleOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "SetTitleOperation", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Hash() + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(git.Hash) + rctx.Result = res + return res +} + +// nolint: vetshadow func (ec *executionContext) _SetTitleOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) graphql.Marshaler { rctx := &graphql.ResolverContext{ Object: "SetTitleOperation", @@ -4201,6 +4808,286 @@ func (ec *executionContext) _SetTitleOperation_was(ctx context.Context, field gr return graphql.MarshalString(res) } +var timelineItemConnectionImplementors = []string{"TimelineItemConnection"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _TimelineItemConnection(ctx context.Context, sel ast.SelectionSet, obj *models.TimelineItemConnection) graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, timelineItemConnectionImplementors) + + out := graphql.NewOrderedMap(len(fields)) + invalid := false + for i, field := range fields { + out.Keys[i] = field.Alias + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TimelineItemConnection") + case "edges": + out.Values[i] = ec._TimelineItemConnection_edges(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "nodes": + out.Values[i] = ec._TimelineItemConnection_nodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "pageInfo": + out.Values[i] = ec._TimelineItemConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "totalCount": + out.Values[i] = ec._TimelineItemConnection_totalCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + if invalid { + return graphql.Null + } + return out +} + +// nolint: vetshadow +func (ec *executionContext) _TimelineItemConnection_edges(ctx context.Context, field graphql.CollectedField, obj *models.TimelineItemConnection) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "TimelineItemConnection", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Edges, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]models.TimelineItemEdge) + rctx.Result = res + + arr1 := make(graphql.Array, len(res)) + var wg sync.WaitGroup + + isLen1 := len(res) == 1 + if !isLen1 { + wg.Add(len(res)) + } + + for idx1 := range res { + idx1 := idx1 + rctx := &graphql.ResolverContext{ + Index: &idx1, + Result: &res[idx1], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(idx1 int) { + if !isLen1 { + defer wg.Done() + } + arr1[idx1] = func() graphql.Marshaler { + + return ec._TimelineItemEdge(ctx, field.Selections, &res[idx1]) + }() + } + if isLen1 { + f(idx1) + } else { + go f(idx1) + } + + } + wg.Wait() + return arr1 +} + +// nolint: vetshadow +func (ec *executionContext) _TimelineItemConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *models.TimelineItemConnection) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "TimelineItemConnection", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Nodes, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]bug.TimelineItem) + rctx.Result = res + + arr1 := make(graphql.Array, len(res)) + var wg sync.WaitGroup + + isLen1 := len(res) == 1 + if !isLen1 { + wg.Add(len(res)) + } + + for idx1 := range res { + idx1 := idx1 + rctx := &graphql.ResolverContext{ + Index: &idx1, + Result: &res[idx1], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(idx1 int) { + if !isLen1 { + defer wg.Done() + } + arr1[idx1] = func() graphql.Marshaler { + + return ec._TimelineItem(ctx, field.Selections, &res[idx1]) + }() + } + if isLen1 { + f(idx1) + } else { + go f(idx1) + } + + } + wg.Wait() + return arr1 +} + +// nolint: vetshadow +func (ec *executionContext) _TimelineItemConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.TimelineItemConnection) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "TimelineItemConnection", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.PageInfo, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.PageInfo) + rctx.Result = res + + return ec._PageInfo(ctx, field.Selections, &res) +} + +// nolint: vetshadow +func (ec *executionContext) _TimelineItemConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.TimelineItemConnection) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "TimelineItemConnection", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.TotalCount, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + rctx.Result = res + return graphql.MarshalInt(res) +} + +var timelineItemEdgeImplementors = []string{"TimelineItemEdge"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _TimelineItemEdge(ctx context.Context, sel ast.SelectionSet, obj *models.TimelineItemEdge) graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, timelineItemEdgeImplementors) + + out := graphql.NewOrderedMap(len(fields)) + invalid := false + for i, field := range fields { + out.Keys[i] = field.Alias + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TimelineItemEdge") + case "cursor": + out.Values[i] = ec._TimelineItemEdge_cursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "node": + out.Values[i] = ec._TimelineItemEdge_node(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + if invalid { + return graphql.Null + } + return out +} + +// nolint: vetshadow +func (ec *executionContext) _TimelineItemEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *models.TimelineItemEdge) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "TimelineItemEdge", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Cursor, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + return graphql.MarshalString(res) +} + +// nolint: vetshadow +func (ec *executionContext) _TimelineItemEdge_node(ctx context.Context, field graphql.CollectedField, obj *models.TimelineItemEdge) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "TimelineItemEdge", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.Node, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bug.TimelineItem) + rctx.Result = res + + return ec._TimelineItem(ctx, field.Selections, &res) +} + var __DirectiveImplementors = []string{"__Directive"} // nolint: gocyclo, errcheck, gas, goconst @@ -5515,16 +6402,35 @@ func (ec *executionContext) _Operation(ctx context.Context, sel ast.SelectionSet switch obj := (*obj).(type) { case nil: return graphql.Null - case bug.CreateOperation: - return ec._CreateOperation(ctx, sel, &obj) - case bug.SetTitleOperation: - return ec._SetTitleOperation(ctx, sel, &obj) - case bug.AddCommentOperation: - return ec._AddCommentOperation(ctx, sel, &obj) - case bug.SetStatusOperation: - return ec._SetStatusOperation(ctx, sel, &obj) - case bug.LabelChangeOperation: - return ec._LabelChangeOperation(ctx, sel, &obj) + case *bug.CreateOperation: + return ec._CreateOperation(ctx, sel, obj) + case *bug.SetTitleOperation: + return ec._SetTitleOperation(ctx, sel, obj) + case *bug.AddCommentOperation: + return ec._AddCommentOperation(ctx, sel, obj) + case *bug.SetStatusOperation: + return ec._SetStatusOperation(ctx, sel, obj) + case *bug.LabelChangeOperation: + return ec._LabelChangeOperation(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + +func (ec *executionContext) _TimelineItem(ctx context.Context, sel ast.SelectionSet, obj *bug.TimelineItem) graphql.Marshaler { + switch obj := (*obj).(type) { + case nil: + return graphql.Null + case *bug.SetTitleOperation: + return ec._SetTitleOperation(ctx, sel, obj) + case *bug.SetStatusOperation: + return ec._SetStatusOperation(ctx, sel, obj) + case *bug.LabelChangeOperation: + return ec._LabelChangeOperation(ctx, sel, obj) + case *bug.CreateTimelineItem: + return ec._CreateTimelineItem(ctx, sel, obj) + case *bug.CommentTimelineItem: + return ec._CommentTimelineItem(ctx, sel, obj) default: panic(fmt.Errorf("unexpected type %T", obj)) } @@ -5629,6 +6535,11 @@ type OperationEdge { node: Operation! } +"""An item in the timeline of events""" +interface TimelineItem { + hash: Hash! +} + """An operation applied to a bug.""" interface Operation { """The operations author.""" @@ -5646,7 +6557,8 @@ type CreateOperation implements Operation & Authored { files: [Hash!]! } -type SetTitleOperation implements Operation & Authored { +type SetTitleOperation implements Operation & Authored & TimelineItem { + hash: Hash! author: Person! date: Time! @@ -5662,14 +6574,16 @@ type AddCommentOperation implements Operation & Authored { files: [Hash!]! } -type SetStatusOperation implements Operation & Authored { +type SetStatusOperation implements Operation & Authored & TimelineItem { + hash: Hash! author: Person! date: Time! status: Status! } -type LabelChangeOperation implements Operation & Authored { +type LabelChangeOperation implements Operation & Authored & TimelineItem { + hash: Hash! author: Person! date: Time! @@ -5677,6 +6591,30 @@ type LabelChangeOperation implements Operation & Authored { removed: [Label!]! } +type TimelineItemConnection { + edges: [TimelineItemEdge!]! + nodes: [TimelineItem!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type TimelineItemEdge { + cursor: String! + node: TimelineItem! +} + +type CreateTimelineItem implements TimelineItem { + hash: Hash! + lastState: Comment! + history: [Comment!]! +} + +type CommentTimelineItem implements TimelineItem { + hash: Hash! + lastState: Comment! + history: [Comment!]! +} + """The connection type for Bug.""" type BugConnection { """A list of edges.""" @@ -5717,6 +6655,17 @@ type Bug { last: Int ): CommentConnection! + timeline( + """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 + ): TimelineItemConnection! + operations( """Returns the elements in the list that come after the specified cursor.""" after: String diff --git a/graphql/models/edges.go b/graphql/models/edges.go index 7de76edf..1dc42583 100644 --- a/graphql/models/edges.go +++ b/graphql/models/edges.go @@ -14,3 +14,8 @@ func (e BugEdge) GetCursor() string { func (e CommentEdge) GetCursor() string { return e.Cursor } + +// GetCursor return the cursor entry of an edge +func (e TimelineItemEdge) GetCursor() string { + return e.Cursor +} diff --git a/graphql/models/gen_models.go b/graphql/models/gen_models.go index 23a25814..849b1842 100644 --- a/graphql/models/gen_models.go +++ b/graphql/models/gen_models.go @@ -59,6 +59,18 @@ type PageInfo struct { EndCursor string `json:"endCursor"` } +type TimelineItemConnection struct { + Edges []TimelineItemEdge `json:"edges"` + Nodes []bug.TimelineItem `json:"nodes"` + PageInfo PageInfo `json:"pageInfo"` + TotalCount int `json:"totalCount"` +} + +type TimelineItemEdge struct { + Cursor string `json:"cursor"` + Node bug.TimelineItem `json:"node"` +} + type Status string const ( diff --git a/graphql/resolvers/bug.go b/graphql/resolvers/bug.go index 858feb16..76eed55d 100644 --- a/graphql/resolvers/bug.go +++ b/graphql/resolvers/bug.go @@ -69,6 +69,33 @@ func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *str return connections.BugOperationCon(obj.Operations, edger, conMaker, input) } +func (bugResolver) Timeline(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (models.TimelineItemConnection, error) { + input := models.ConnectionInput{ + Before: before, + After: after, + First: first, + Last: last, + } + + edger := func(op bug.TimelineItem, offset int) connections.Edge { + return models.TimelineItemEdge{ + Node: op, + Cursor: connections.OffsetToCursor(offset), + } + } + + conMaker := func(edges []models.TimelineItemEdge, nodes []bug.TimelineItem, info models.PageInfo, totalCount int) (models.TimelineItemConnection, error) { + return models.TimelineItemConnection{ + Edges: edges, + Nodes: nodes, + PageInfo: info, + TotalCount: totalCount, + }, nil + } + + return connections.BugTimelineItemCon(obj.Timeline, edger, conMaker, input) +} + func (bugResolver) LastEdit(ctx context.Context, obj *bug.Snapshot) (time.Time, error) { return obj.LastEditTime(), nil } diff --git a/graphql/schema.graphql b/graphql/schema.graphql index c083ac4f..2e685779 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -73,6 +73,11 @@ type OperationEdge { node: Operation! } +"""An item in the timeline of events""" +interface TimelineItem { + hash: Hash! +} + """An operation applied to a bug.""" interface Operation { """The operations author.""" @@ -90,7 +95,8 @@ type CreateOperation implements Operation & Authored { files: [Hash!]! } -type SetTitleOperation implements Operation & Authored { +type SetTitleOperation implements Operation & Authored & TimelineItem { + hash: Hash! author: Person! date: Time! @@ -106,14 +112,16 @@ type AddCommentOperation implements Operation & Authored { files: [Hash!]! } -type SetStatusOperation implements Operation & Authored { +type SetStatusOperation implements Operation & Authored & TimelineItem { + hash: Hash! author: Person! date: Time! status: Status! } -type LabelChangeOperation implements Operation & Authored { +type LabelChangeOperation implements Operation & Authored & TimelineItem { + hash: Hash! author: Person! date: Time! @@ -121,6 +129,30 @@ type LabelChangeOperation implements Operation & Authored { removed: [Label!]! } +type TimelineItemConnection { + edges: [TimelineItemEdge!]! + nodes: [TimelineItem!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type TimelineItemEdge { + cursor: String! + node: TimelineItem! +} + +type CreateTimelineItem implements TimelineItem { + hash: Hash! + lastState: Comment! + history: [Comment!]! +} + +type CommentTimelineItem implements TimelineItem { + hash: Hash! + lastState: Comment! + history: [Comment!]! +} + """The connection type for Bug.""" type BugConnection { """A list of edges.""" @@ -161,6 +193,17 @@ type Bug { last: Int ): CommentConnection! + timeline( + """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 + ): TimelineItemConnection! + operations( """Returns the elements in the list that come after the specified cursor.""" after: String |