aboutsummaryrefslogtreecommitdiffstats
path: root/graphql
diff options
context:
space:
mode:
Diffstat (limited to 'graphql')
-rw-r--r--graphql/connections/connection_template.go82
-rw-r--r--graphql/connections/connections.go39
-rw-r--r--graphql/connections/gen_bug.go83
-rw-r--r--graphql/connections/gen_comment.go83
-rw-r--r--graphql/connections/gen_operation.go83
-rw-r--r--graphql/gqlgen.yml2
-rw-r--r--graphql/models/edges.go13
-rw-r--r--graphql/models/gen_models.go (renamed from graphql/resolvers/gen_model.go)8
-rw-r--r--graphql/resolvers/bug.go58
-rw-r--r--graphql/resolvers/gen_graph.go118
-rw-r--r--graphql/resolvers/operations.go9
-rw-r--r--graphql/resolvers/pager_bug.go225
-rw-r--r--graphql/resolvers/pager_comment.go225
-rw-r--r--graphql/resolvers/pager_operation.go225
-rw-r--r--graphql/resolvers/pagers.go51
-rw-r--r--graphql/resolvers/pagers_template.go224
-rw-r--r--graphql/resolvers/repo.go3
-rw-r--r--graphql/schema.graphql2
18 files changed, 480 insertions, 1053 deletions
diff --git a/graphql/connections/connection_template.go b/graphql/connections/connection_template.go
new file mode 100644
index 00000000..3dfaca8f
--- /dev/null
+++ b/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
+}
diff --git a/graphql/connections/connections.go b/graphql/connections/connections.go
new file mode 100644
index 00000000..145bfbbe
--- /dev/null
+++ b/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
+}
diff --git a/graphql/connections/gen_bug.go b/graphql/connections/gen_bug.go
new file mode 100644
index 00000000..b43d5c11
--- /dev/null
+++ b/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
+}
diff --git a/graphql/connections/gen_comment.go b/graphql/connections/gen_comment.go
new file mode 100644
index 00000000..dfcce42a
--- /dev/null
+++ b/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
+}
diff --git a/graphql/connections/gen_operation.go b/graphql/connections/gen_operation.go
new file mode 100644
index 00000000..32cee97a
--- /dev/null
+++ b/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
+}
diff --git a/graphql/gqlgen.yml b/graphql/gqlgen.yml
index 131a8b58..b0b89ab7 100644
--- a/graphql/gqlgen.yml
+++ b/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:
diff --git a/graphql/models/edges.go b/graphql/models/edges.go
new file mode 100644
index 00000000..677d13f5
--- /dev/null
+++ b/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
+}
diff --git a/graphql/resolvers/gen_model.go b/graphql/models/gen_models.go
index f6d78471..a59288ec 100644
--- a/graphql/resolvers/gen_model.go
+++ b/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"`
diff --git a/graphql/resolvers/bug.go b/graphql/resolvers/bug.go
index ad6c288b..246507c6 100644
--- a/graphql/resolvers/bug.go
+++ b/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)
}
diff --git a/graphql/resolvers/gen_graph.go b/graphql/resolvers/gen_graph.go
index 3d752ddc..409b7e42 100644
--- a/graphql/resolvers/gen_graph.go
+++ b/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!
diff --git a/graphql/resolvers/operations.go b/graphql/resolvers/operations.go
index 1279a1b4..7fb3f3e2 100644
--- a/graphql/resolvers/operations.go
+++ b/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")
diff --git a/graphql/resolvers/pager_bug.go b/graphql/resolvers/pager_bug.go
deleted file mode 100644
index 55acfcc4..00000000
--- a/graphql/resolvers/pager_bug.go
+++ /dev/null
@@ -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
diff --git a/graphql/resolvers/pager_comment.go b/graphql/resolvers/pager_comment.go
deleted file mode 100644
index 3dd11757..00000000
--- a/graphql/resolvers/pager_comment.go
+++ /dev/null
@@ -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
diff --git a/graphql/resolvers/pager_operation.go b/graphql/resolvers/pager_operation.go
deleted file mode 100644
index fe4eebc2..00000000
--- a/graphql/resolvers/pager_operation.go
+++ /dev/null
@@ -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
diff --git a/graphql/resolvers/pagers.go b/graphql/resolvers/pagers.go
deleted file mode 100644
index 378dcdbf..00000000
--- a/graphql/resolvers/pagers.go
+++ /dev/null
@@ -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
-}
diff --git a/graphql/resolvers/pagers_template.go b/graphql/resolvers/pagers_template.go
deleted file mode 100644
index 0ca7de75..00000000
--- a/graphql/resolvers/pagers_template.go
+++ /dev/null
@@ -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
-//}
diff --git a/graphql/resolvers/repo.go b/graphql/resolvers/repo.go
index 14019b65..d8190f15 100644
--- a/graphql/resolvers/repo.go
+++ b/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")
}
diff --git a/graphql/schema.graphql b/graphql/schema.graphql
index 47716488..14b2bec5 100644
--- a/graphql/schema.graphql
+++ b/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!