graphql: expose allIdentities, identities and userIdentity in the repo

This commit is contained in:
Michael Muré 2019-03-31 21:44:14 +02:00 committed by Michael Muré
parent e028b895aa
commit 15c258cdc4
No known key found for this signature in database
GPG Key ID: A4457C029293126F
26 changed files with 1118 additions and 127 deletions

6
Gopkg.lock generated
View File

@ -2,7 +2,7 @@
[[projects]]
digest = "1:cc0bbbf01f9f639555f9a9d4d5ff9c72da4c4c4bfe71f02fd318d37498270d80"
digest = "1:e8f6639eaa399c8595b9a2dee175514a9f3842888dc080e2776360dc604150dc"
name = "github.com/99designs/gqlgen"
packages = [
"cmd",
@ -16,8 +16,8 @@
"internal/imports",
]
pruneopts = "UT"
revision = "3a7f37c7e22a8fedce430c4d340ad5c1351198f4"
version = "v0.7.1"
revision = "da1e07f5876c0fb79cbad19006f7135be08590d6"
version = "v0.7.2"
[[projects]]
branch = "master"

View File

@ -7,6 +7,9 @@ import (
"github.com/cheekybits/genny/generic"
)
// Name define the name of the connection
type Name generic.Type
// NodeType define the node type handled by this relay connection
type NodeType generic.Type
@ -18,17 +21,17 @@ type ConnectionType generic.Type
// NodeTypeEdgeMaker define a function that take a NodeType and an offset and
// create an Edge.
type NodeTypeEdgeMaker func(value NodeType, offset int) Edge
type NameEdgeMaker func(value NodeType, offset int) Edge
// NodeTypeConMaker define a function that create a ConnectionType
type NodeTypeConMaker func(
// NameConMaker define a function that create a ConnectionType
type NameConMaker func(
edges []EdgeType,
nodes []NodeType,
info models.PageInfo,
totalCount int) (ConnectionType, error)
// NodeTypeCon will paginate a source according to the input of a relay connection
func NodeTypeCon(source []NodeType, edgeMaker NodeTypeEdgeMaker, conMaker NodeTypeConMaker, input models.ConnectionInput) (ConnectionType, error) {
// NameCon will paginate a source according to the input of a relay connection
func NameCon(source []NodeType, edgeMaker NameEdgeMaker, conMaker NameConMaker, input models.ConnectionInput) (ConnectionType, error) {
var nodes []NodeType
var edges []EdgeType
var cursors []string

View File

@ -1,7 +1,8 @@
//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"
//go:generate genny -in=connection_template.go -out=gen_bug.go gen "Name=LazyBug NodeType=string EdgeType=LazyBugEdge ConnectionType=models.BugConnection"
//go:generate genny -in=connection_template.go -out=gen_identity.go gen "Name=LazyIdentity NodeType=string EdgeType=LazyIdentityEdge ConnectionType=models.IdentityConnection"
//go:generate genny -in=connection_template.go -out=gen_operation.go gen "Name=Operation NodeType=bug.Operation EdgeType=models.OperationEdge ConnectionType=models.OperationConnection"
//go:generate genny -in=connection_template.go -out=gen_comment.go gen "Name=Comment NodeType=bug.Comment EdgeType=models.CommentEdge ConnectionType=models.CommentConnection"
//go:generate genny -in=connection_template.go -out=gen_timeline.go gen "Name=TimelineItem NodeType=bug.TimelineItem EdgeType=models.TimelineItemEdge ConnectionType=models.TimelineItemConnection"
// Package connections implement a generic GraphQL relay connection
package connections

View File

@ -12,17 +12,17 @@ import (
// StringEdgeMaker define a function that take a string and an offset and
// create an Edge.
type StringEdgeMaker func(value string, offset int) Edge
type LazyBugEdgeMaker func(value string, offset int) Edge
// StringConMaker define a function that create a models.BugConnection
type StringConMaker func(
// LazyBugConMaker define a function that create a models.BugConnection
type LazyBugConMaker func(
edges []LazyBugEdge,
nodes []string,
info models.PageInfo,
totalCount int) (models.BugConnection, error)
// StringCon will paginate a source according to the input of a relay connection
func StringCon(source []string, edgeMaker StringEdgeMaker, conMaker StringConMaker, input models.ConnectionInput) (models.BugConnection, error) {
// LazyBugCon will paginate a source according to the input of a relay connection
func LazyBugCon(source []string, edgeMaker LazyBugEdgeMaker, conMaker LazyBugConMaker, input models.ConnectionInput) (models.BugConnection, error) {
var nodes []string
var edges []LazyBugEdge
var cursors []string

View File

@ -13,17 +13,17 @@ import (
// BugCommentEdgeMaker define a function that take a bug.Comment and an offset and
// create an Edge.
type BugCommentEdgeMaker func(value bug.Comment, offset int) Edge
type CommentEdgeMaker func(value bug.Comment, offset int) Edge
// BugCommentConMaker define a function that create a models.CommentConnection
type BugCommentConMaker func(
// CommentConMaker define a function that create a models.CommentConnection
type CommentConMaker func(
edges []models.CommentEdge,
nodes []bug.Comment,
info models.PageInfo,
totalCount int) (models.CommentConnection, error)
// BugCommentCon will paginate a source according to the input of a relay connection
func BugCommentCon(source []bug.Comment, edgeMaker BugCommentEdgeMaker, conMaker BugCommentConMaker, input models.ConnectionInput) (models.CommentConnection, error) {
// CommentCon will paginate a source according to the input of a relay connection
func CommentCon(source []bug.Comment, edgeMaker CommentEdgeMaker, conMaker CommentConMaker, input models.ConnectionInput) (models.CommentConnection, error) {
var nodes []bug.Comment
var edges []models.CommentEdge
var cursors []string

View File

@ -0,0 +1,110 @@
// 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/graphql/models"
)
// StringEdgeMaker define a function that take a string and an offset and
// create an Edge.
type LazyIdentityEdgeMaker func(value string, offset int) Edge
// LazyIdentityConMaker define a function that create a models.IdentityConnection
type LazyIdentityConMaker func(
edges []LazyIdentityEdge,
nodes []string,
info models.PageInfo,
totalCount int) (models.IdentityConnection, error)
// LazyIdentityCon will paginate a source according to the input of a relay connection
func LazyIdentityCon(source []string, edgeMaker LazyIdentityEdgeMaker, conMaker LazyIdentityConMaker, input models.ConnectionInput) (models.IdentityConnection, error) {
var nodes []string
var edges []LazyIdentityEdge
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.(LazyIdentityEdge))
cursors = append(cursors, edge.GetCursor())
nodes = append(nodes, value)
}
} else {
edges = make([]LazyIdentityEdge, len(source))
cursors = make([]string, len(source))
nodes = source
for i, value := range source {
edge := edgeMaker(value, i+offset)
edges[i] = edge.(LazyIdentityEdge)
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)
}

View File

@ -13,17 +13,17 @@ import (
// BugOperationEdgeMaker define a function that take a bug.Operation and an offset and
// create an Edge.
type BugOperationEdgeMaker func(value bug.Operation, offset int) Edge
type OperationEdgeMaker func(value bug.Operation, offset int) Edge
// BugOperationConMaker define a function that create a models.OperationConnection
type BugOperationConMaker func(
// OperationConMaker define a function that create a models.OperationConnection
type OperationConMaker func(
edges []models.OperationEdge,
nodes []bug.Operation,
info models.PageInfo,
totalCount int) (models.OperationConnection, error)
// BugOperationCon will paginate a source according to the input of a relay connection
func BugOperationCon(source []bug.Operation, edgeMaker BugOperationEdgeMaker, conMaker BugOperationConMaker, input models.ConnectionInput) (models.OperationConnection, error) {
// OperationCon will paginate a source according to the input of a relay connection
func OperationCon(source []bug.Operation, edgeMaker OperationEdgeMaker, conMaker OperationConMaker, input models.ConnectionInput) (models.OperationConnection, error) {
var nodes []bug.Operation
var edges []models.OperationEdge
var cursors []string

View File

@ -13,17 +13,17 @@ import (
// 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
type TimelineItemEdgeMaker func(value bug.TimelineItem, offset int) Edge
// BugTimelineItemConMaker define a function that create a models.TimelineItemConnection
type BugTimelineItemConMaker func(
// TimelineItemConMaker define a function that create a models.TimelineItemConnection
type TimelineItemConMaker 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) {
// TimelineItemCon will paginate a source according to the input of a relay connection
func TimelineItemCon(source []bug.TimelineItem, edgeMaker TimelineItemEdgeMaker, conMaker TimelineItemConMaker, input models.ConnectionInput) (models.TimelineItemConnection, error) {
var nodes []bug.TimelineItem
var edges []models.TimelineItemEdge
var cursors []string

View File

@ -0,0 +1,12 @@
package connections
// LazyIdentityEdge is a special relay edge used to implement a lazy loading connection
type LazyIdentityEdge struct {
Id string
Cursor string
}
// GetCursor return the cursor of a LazyIdentityEdge
func (lbe LazyIdentityEdge) GetCursor() string {
return lbe.Cursor
}

View File

@ -161,6 +161,7 @@ type ComplexityRoot struct {
Identity struct {
Id func(childComplexity int) int
HumanId func(childComplexity int) int
Name func(childComplexity int) int
Email func(childComplexity int) int
Login func(childComplexity int) int
@ -169,6 +170,18 @@ type ComplexityRoot struct {
IsProtected func(childComplexity int) int
}
IdentityConnection struct {
Edges func(childComplexity int) int
Nodes func(childComplexity int) int
PageInfo func(childComplexity int) int
TotalCount func(childComplexity int) int
}
IdentityEdge struct {
Cursor func(childComplexity int) int
Node func(childComplexity int) int
}
LabelChangeOperation struct {
Hash func(childComplexity int) int
Author func(childComplexity int) int
@ -220,8 +233,11 @@ type ComplexityRoot struct {
}
Repository struct {
AllBugs func(childComplexity int, after *string, before *string, first *int, last *int, query *string) int
Bug func(childComplexity int, prefix string) int
AllBugs func(childComplexity int, after *string, before *string, first *int, last *int, query *string) int
Bug func(childComplexity int, prefix string) int
AllIdentities func(childComplexity int, after *string, before *string, first *int, last *int) int
Identity func(childComplexity int, prefix string) int
UserIdentity func(childComplexity int) int
}
SetStatusOperation struct {
@ -297,6 +313,7 @@ type EditCommentOperationResolver interface {
}
type IdentityResolver interface {
ID(ctx context.Context, obj *identity.Interface) (string, error)
HumanID(ctx context.Context, obj *identity.Interface) (string, error)
Name(ctx context.Context, obj *identity.Interface) (*string, error)
Email(ctx context.Context, obj *identity.Interface) (*string, error)
Login(ctx context.Context, obj *identity.Interface) (*string, error)
@ -326,6 +343,9 @@ type QueryResolver interface {
type RepositoryResolver interface {
AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (models.BugConnection, error)
Bug(ctx context.Context, obj *models.Repository, prefix string) (*bug.Snapshot, error)
AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (models.IdentityConnection, error)
Identity(ctx context.Context, obj *models.Repository, prefix string) (*identity.Interface, error)
UserIdentity(ctx context.Context, obj *models.Repository) (*identity.Interface, error)
}
type SetStatusOperationResolver interface {
Date(ctx context.Context, obj *bug.SetStatusOperation) (time.Time, error)
@ -959,6 +979,83 @@ func field_Repository_bug_args(rawArgs map[string]interface{}) (map[string]inter
}
func field_Repository_allIdentities_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_Repository_identity_args(rawArgs map[string]interface{}) (map[string]interface{}, error) {
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["prefix"]; ok {
var err error
arg0, err = graphql.UnmarshalString(tmp)
if err != nil {
return nil, err
}
}
args["prefix"] = arg0
return args, nil
}
func field___Type_fields_args(rawArgs map[string]interface{}) (map[string]interface{}, error) {
args := map[string]interface{}{}
var arg0 bool
@ -1465,6 +1562,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Identity.Id(childComplexity), true
case "Identity.humanId":
if e.complexity.Identity.HumanId == nil {
break
}
return e.complexity.Identity.HumanId(childComplexity), true
case "Identity.name":
if e.complexity.Identity.Name == nil {
break
@ -1507,6 +1611,48 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Identity.IsProtected(childComplexity), true
case "IdentityConnection.edges":
if e.complexity.IdentityConnection.Edges == nil {
break
}
return e.complexity.IdentityConnection.Edges(childComplexity), true
case "IdentityConnection.nodes":
if e.complexity.IdentityConnection.Nodes == nil {
break
}
return e.complexity.IdentityConnection.Nodes(childComplexity), true
case "IdentityConnection.pageInfo":
if e.complexity.IdentityConnection.PageInfo == nil {
break
}
return e.complexity.IdentityConnection.PageInfo(childComplexity), true
case "IdentityConnection.totalCount":
if e.complexity.IdentityConnection.TotalCount == nil {
break
}
return e.complexity.IdentityConnection.TotalCount(childComplexity), true
case "IdentityEdge.cursor":
if e.complexity.IdentityEdge.Cursor == nil {
break
}
return e.complexity.IdentityEdge.Cursor(childComplexity), true
case "IdentityEdge.node":
if e.complexity.IdentityEdge.Node == nil {
break
}
return e.complexity.IdentityEdge.Node(childComplexity), true
case "LabelChangeOperation.hash":
if e.complexity.LabelChangeOperation.Hash == nil {
break
@ -1774,6 +1920,37 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Repository.Bug(childComplexity, args["prefix"].(string)), true
case "Repository.allIdentities":
if e.complexity.Repository.AllIdentities == nil {
break
}
args, err := field_Repository_allIdentities_args(rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Repository.AllIdentities(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true
case "Repository.identity":
if e.complexity.Repository.Identity == nil {
break
}
args, err := field_Repository_identity_args(rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Repository.Identity(childComplexity, args["prefix"].(string)), true
case "Repository.userIdentity":
if e.complexity.Repository.UserIdentity == nil {
break
}
return e.complexity.Repository.UserIdentity(childComplexity), true
case "SetStatusOperation.hash":
if e.complexity.SetStatusOperation.Hash == nil {
break
@ -4680,6 +4857,15 @@ func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet,
}
wg.Done()
}(i, field)
case "humanId":
wg.Add(1)
go func(i int, field graphql.CollectedField) {
out.Values[i] = ec._Identity_humanId(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalid = true
}
wg.Done()
}(i, field)
case "name":
wg.Add(1)
go func(i int, field graphql.CollectedField) {
@ -4760,6 +4946,33 @@ func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.Coll
return graphql.MarshalString(res)
}
// nolint: vetshadow
func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql.CollectedField, obj *identity.Interface) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "Identity",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Identity().HumanID(rctx, obj)
})
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return graphql.MarshalString(res)
}
// nolint: vetshadow
func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.CollectedField, obj *identity.Interface) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
@ -4926,6 +5139,316 @@ func (ec *executionContext) _Identity_isProtected(ctx context.Context, field gra
return graphql.MarshalBoolean(res)
}
var identityConnectionImplementors = []string{"IdentityConnection"}
// nolint: gocyclo, errcheck, gas, goconst
func (ec *executionContext) _IdentityConnection(ctx context.Context, sel ast.SelectionSet, obj *models.IdentityConnection) graphql.Marshaler {
fields := graphql.CollectFields(ctx, sel, identityConnectionImplementors)
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("IdentityConnection")
case "edges":
out.Values[i] = ec._IdentityConnection_edges(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalid = true
}
case "nodes":
out.Values[i] = ec._IdentityConnection_nodes(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalid = true
}
case "pageInfo":
out.Values[i] = ec._IdentityConnection_pageInfo(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalid = true
}
case "totalCount":
out.Values[i] = ec._IdentityConnection_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) _IdentityConnection_edges(ctx context.Context, field graphql.CollectedField, obj *models.IdentityConnection) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "IdentityConnection",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Edges, nil
})
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.([]models.IdentityEdge)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
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._IdentityEdge(ctx, field.Selections, &res[idx1])
}()
}
if isLen1 {
f(idx1)
} else {
go f(idx1)
}
}
wg.Wait()
return arr1
}
// nolint: vetshadow
func (ec *executionContext) _IdentityConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *models.IdentityConnection) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "IdentityConnection",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Nodes, nil
})
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.([]identity.Interface)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
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._Identity(ctx, field.Selections, &res[idx1])
}()
}
if isLen1 {
f(idx1)
} else {
go f(idx1)
}
}
wg.Wait()
return arr1
}
// nolint: vetshadow
func (ec *executionContext) _IdentityConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.IdentityConnection) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "IdentityConnection",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
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
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec._PageInfo(ctx, field.Selections, &res)
}
// nolint: vetshadow
func (ec *executionContext) _IdentityConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.IdentityConnection) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "IdentityConnection",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
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
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return graphql.MarshalInt(res)
}
var identityEdgeImplementors = []string{"IdentityEdge"}
// nolint: gocyclo, errcheck, gas, goconst
func (ec *executionContext) _IdentityEdge(ctx context.Context, sel ast.SelectionSet, obj *models.IdentityEdge) graphql.Marshaler {
fields := graphql.CollectFields(ctx, sel, identityEdgeImplementors)
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("IdentityEdge")
case "cursor":
out.Values[i] = ec._IdentityEdge_cursor(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalid = true
}
case "node":
out.Values[i] = ec._IdentityEdge_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) _IdentityEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *models.IdentityEdge) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "IdentityEdge",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
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
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return graphql.MarshalString(res)
}
// nolint: vetshadow
func (ec *executionContext) _IdentityEdge_node(ctx context.Context, field graphql.CollectedField, obj *models.IdentityEdge) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "IdentityEdge",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Node, nil
})
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(identity.Interface)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec._Identity(ctx, field.Selections, &res)
}
var labelChangeOperationImplementors = []string{"LabelChangeOperation", "Operation", "Authored"}
// nolint: gocyclo, errcheck, gas, goconst
@ -6313,6 +6836,27 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe
out.Values[i] = ec._Repository_bug(ctx, field, obj)
wg.Done()
}(i, field)
case "allIdentities":
wg.Add(1)
go func(i int, field graphql.CollectedField) {
out.Values[i] = ec._Repository_allIdentities(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalid = true
}
wg.Done()
}(i, field)
case "identity":
wg.Add(1)
go func(i int, field graphql.CollectedField) {
out.Values[i] = ec._Repository_identity(ctx, field, obj)
wg.Done()
}(i, field)
case "userIdentity":
wg.Add(1)
go func(i int, field graphql.CollectedField) {
out.Values[i] = ec._Repository_userIdentity(ctx, field, obj)
wg.Done()
}(i, field)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -6393,6 +6937,104 @@ func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.C
return ec._Bug(ctx, field.Selections, res)
}
// nolint: vetshadow
func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field graphql.CollectedField, obj *models.Repository) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rawArgs := field.ArgumentMap(ec.Variables)
args, err := field_Repository_allIdentities_args(rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
rctx := &graphql.ResolverContext{
Object: "Repository",
Args: args,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Repository().AllIdentities(rctx, 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.IdentityConnection)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec._IdentityConnection(ctx, field.Selections, &res)
}
// nolint: vetshadow
func (ec *executionContext) _Repository_identity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rawArgs := field.ArgumentMap(ec.Variables)
args, err := field_Repository_identity_args(rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
rctx := &graphql.ResolverContext{
Object: "Repository",
Args: args,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Repository().Identity(rctx, obj, args["prefix"].(string))
})
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*identity.Interface)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
if res == nil {
return graphql.Null
}
return ec._Identity(ctx, field.Selections, res)
}
// nolint: vetshadow
func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
rctx := &graphql.ResolverContext{
Object: "Repository",
Args: nil,
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Repository().UserIdentity(rctx, obj)
})
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*identity.Interface)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
if res == nil {
return graphql.Null
}
return ec._Identity(ctx, field.Selections, res)
}
var setStatusOperationImplementors = []string{"SetStatusOperation", "Operation", "Authored"}
// nolint: gocyclo, errcheck, gas, goconst
@ -8987,7 +9629,9 @@ enum Status {
}
type Bug {
"""The identifier for this bug"""
id: String!
"""The human version (truncated) identifier for this bug"""
humanId: String!
status: Status!
title: String!
@ -9049,26 +9693,13 @@ type BugEdge {
node: Bug!
}
type Repository {
allBugs(
"""Returns the elements in the list that come after the specified cursor."""
after: String
"""Returns the elements in the list that come before the specified cursor."""
before: String
"""Returns the first _n_ elements from the list."""
first: Int
"""Returns the last _n_ elements from the list."""
last: Int
"""A query to select and order bugs"""
query: String
): BugConnection!
bug(prefix: String!): Bug
}
`},
&ast.Source{Name: "schema/identity.graphql", Input: `"""Represents an identity"""
type Identity {
"""The identifier for this identity"""
id: String!
"""The human version (truncated) identifier for this identity"""
humanId: String!
"""The name of the person, if known."""
name: String
"""The email of the person, if known."""
@ -9082,6 +9713,18 @@ type Identity {
"""isProtected is true if the chain of git commits started to be signed.
If that's the case, only signed commit with a valid key for this identity can be added."""
isProtected: Boolean!
}
type IdentityConnection {
edges: [IdentityEdge!]!
nodes: [Identity!]!
pageInfo: PageInfo!
totalCount: Int!
}
type IdentityEdge {
cursor: String!
node: Identity!
}`},
&ast.Source{Name: "schema/operations.graphql", Input: `"""An operation applied to a bug."""
interface Operation {
@ -9184,29 +9827,42 @@ type LabelChangeOperation implements Operation & Authored {
removed: [Label!]!
}
`},
&ast.Source{Name: "schema/root.graphql", Input: `scalar Time
scalar Label
scalar Hash
&ast.Source{Name: "schema/repository.graphql", Input: `
type Repository {
"""All the bugs"""
allBugs(
"""Returns the elements in the list that come after the specified cursor."""
after: String
"""Returns the elements in the list that come before the specified cursor."""
before: String
"""Returns the first _n_ elements from the list."""
first: Int
"""Returns the last _n_ elements from the list."""
last: Int
"""A query to select and order bugs"""
query: String
): BugConnection!
"""Information about pagination in a connection."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!
"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!
"""When paginating backwards, the cursor to continue."""
startCursor: String!
"""When paginating forwards, the cursor to continue."""
endCursor: String!
}
bug(prefix: String!): Bug
"""An object that has an author."""
interface Authored {
"""The author of this object."""
author: Identity!
}
"""All the identities"""
allIdentities(
"""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
): IdentityConnection!
type Query {
identity(prefix: String!):Identity
"""The identity created or selected by the user as its own"""
userIdentity:Identity
}`},
&ast.Source{Name: "schema/root.graphql", Input: `type Query {
defaultRepository: Repository
repository(id: String!): Repository
}
@ -9310,4 +9966,25 @@ type SetTitleTimelineItem implements TimelineItem {
was: String!
}
`},
&ast.Source{Name: "schema/types.graphql", Input: `scalar Time
scalar Label
scalar Hash
"""Information about pagination in a connection."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!
"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!
"""When paginating backwards, the cursor to continue."""
startCursor: String!
"""When paginating forwards, the cursor to continue."""
endCursor: String!
}
"""An object that has an author."""
interface Authored {
"""The author of this object."""
author: Identity!
}`},
)

View File

@ -19,3 +19,8 @@ func (e CommentEdge) GetCursor() string {
func (e TimelineItemEdge) GetCursor() string {
return e.Cursor
}
// GetCursor return the cursor entry of an edge
func (e IdentityEdge) GetCursor() string {
return e.Cursor
}

View File

@ -8,6 +8,7 @@ import (
"strconv"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/identity"
)
// An object that has an author.
@ -41,6 +42,18 @@ type CommentEdge struct {
Node bug.Comment `json:"node"`
}
type IdentityConnection struct {
Edges []IdentityEdge `json:"edges"`
Nodes []identity.Interface `json:"nodes"`
PageInfo PageInfo `json:"pageInfo"`
TotalCount int `json:"totalCount"`
}
type IdentityEdge struct {
Cursor string `json:"cursor"`
Node identity.Interface `json:"node"`
}
// The connection type for an Operation
type OperationConnection struct {
Edges []OperationEdge `json:"edges"`

View File

@ -6,9 +6,12 @@ import (
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/graphql/connections"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
)
var _ graph.BugResolver = &bugResolver{}
type bugResolver struct{}
func (bugResolver) Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error) {
@ -39,7 +42,7 @@ func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, after *strin
}, nil
}
return connections.BugCommentCon(obj.Comments, edger, conMaker, input)
return connections.CommentCon(obj.Comments, edger, conMaker, input)
}
func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (models.OperationConnection, error) {
@ -66,7 +69,7 @@ func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *str
}, nil
}
return connections.BugOperationCon(obj.Operations, edger, conMaker, input)
return connections.OperationCon(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) {
@ -93,7 +96,7 @@ func (bugResolver) Timeline(ctx context.Context, obj *bug.Snapshot, after *strin
}, nil
}
return connections.BugTimelineItemCon(obj.Timeline, edger, conMaker, input)
return connections.TimelineItemCon(obj.Timeline, edger, conMaker, input)
}
func (bugResolver) LastEdit(ctx context.Context, obj *bug.Snapshot) (time.Time, error) {

View File

@ -3,15 +3,22 @@ package resolvers
import (
"context"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/identity"
)
var _ graph.IdentityResolver = &identityResolver{}
type identityResolver struct{}
func (identityResolver) ID(ctx context.Context, obj *identity.Interface) (string, error) {
return (*obj).Id(), nil
}
func (identityResolver) HumanID(ctx context.Context, obj *identity.Interface) (string, error) {
return (*obj).HumanId(), nil
}
func (identityResolver) Name(ctx context.Context, obj *identity.Interface) (*string, error) {
return nilIfEmpty((*obj).Name())
}

View File

@ -5,9 +5,12 @@ import (
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/util/git"
)
var _ graph.MutationResolver = &mutationResolver{}
type mutationResolver struct {
cache *cache.MultiRepoCache
}

View File

@ -4,9 +4,12 @@ import (
"context"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
)
var _ graph.QueryResolver = &rootQueryResolver{}
type rootQueryResolver struct {
cache *cache.MultiRepoCache
}

View File

@ -6,9 +6,13 @@ import (
"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/graph"
"github.com/MichaelMure/git-bug/graphql/models"
"github.com/MichaelMure/git-bug/identity"
)
var _ graph.RepositoryResolver = &repoResolver{}
type repoResolver struct{}
func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (models.BugConnection, error) {
@ -70,7 +74,7 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *
}, nil
}
return connections.StringCon(source, edger, conMaker, input)
return connections.LazyBugCon(source, edger, conMaker, input)
}
func (repoResolver) Bug(ctx context.Context, obj *models.Repository, prefix string) (*bug.Snapshot, error) {
@ -82,3 +86,78 @@ func (repoResolver) Bug(ctx context.Context, obj *models.Repository, prefix stri
return b.Snapshot(), nil
}
func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (models.IdentityConnection, error) {
input := models.ConnectionInput{
Before: before,
After: after,
First: first,
Last: last,
}
// Simply pass a []string with the ids to the pagination algorithm
source := obj.Repo.AllIdentityIds()
// The edger create a custom edge holding just the id
edger := func(id string, offset int) connections.Edge {
return connections.LazyIdentityEdge{
Id: id,
Cursor: connections.OffsetToCursor(offset),
}
}
// The conMaker will finally load and compile identities from git to replace the selected edges
conMaker := func(lazyIdentityEdges []connections.LazyIdentityEdge, lazyNode []string, info models.PageInfo, totalCount int) (models.IdentityConnection, error) {
edges := make([]models.IdentityEdge, len(lazyIdentityEdges))
nodes := make([]identity.Interface, len(lazyIdentityEdges))
for k, lazyIdentityEdge := range lazyIdentityEdges {
i, err := obj.Repo.ResolveIdentity(lazyIdentityEdge.Id)
if err != nil {
return models.IdentityConnection{}, err
}
ii := identity.Interface(i.Identity)
edges[k] = models.IdentityEdge{
Cursor: lazyIdentityEdge.Cursor,
Node: ii,
}
nodes[k] = ii
}
return models.IdentityConnection{
Edges: edges,
Nodes: nodes,
PageInfo: info,
TotalCount: totalCount,
}, nil
}
return connections.LazyIdentityCon(source, edger, conMaker, input)
}
func (repoResolver) Identity(ctx context.Context, obj *models.Repository, prefix string) (*identity.Interface, error) {
i, err := obj.Repo.ResolveIdentityPrefix(prefix)
if err != nil {
return nil, err
}
ii := identity.Interface(i.Identity)
return &ii, nil
}
func (repoResolver) UserIdentity(ctx context.Context, obj *models.Repository) (*identity.Interface, error) {
i, err := obj.Repo.GetUserIdentity()
if err != nil {
return nil, err
}
ii := identity.Interface(i.Identity)
return &ii, nil
}

View File

@ -6,6 +6,8 @@ import (
"github.com/MichaelMure/git-bug/graphql/graph"
)
var _ graph.ResolverRoot = &RootResolver{}
type RootResolver struct {
cache.MultiRepoCache
}

View File

@ -28,7 +28,9 @@ enum Status {
}
type Bug {
"""The identifier for this bug"""
id: String!
"""The human version (truncated) identifier for this bug"""
humanId: String!
status: Status!
title: String!
@ -90,18 +92,3 @@ type BugEdge {
node: Bug!
}
type Repository {
allBugs(
"""Returns the elements in the list that come after the specified cursor."""
after: String
"""Returns the elements in the list that come before the specified cursor."""
before: String
"""Returns the first _n_ elements from the list."""
first: Int
"""Returns the last _n_ elements from the list."""
last: Int
"""A query to select and order bugs"""
query: String
): BugConnection!
bug(prefix: String!): Bug
}

View File

@ -2,6 +2,8 @@
type Identity {
"""The identifier for this identity"""
id: String!
"""The human version (truncated) identifier for this identity"""
humanId: String!
"""The name of the person, if known."""
name: String
"""The email of the person, if known."""
@ -16,3 +18,15 @@ type Identity {
If that's the case, only signed commit with a valid key for this identity can be added."""
isProtected: Boolean!
}
type IdentityConnection {
edges: [IdentityEdge!]!
nodes: [Identity!]!
pageInfo: PageInfo!
totalCount: Int!
}
type IdentityEdge {
cursor: String!
node: Identity!
}

View File

@ -0,0 +1,35 @@
type Repository {
"""All the bugs"""
allBugs(
"""Returns the elements in the list that come after the specified cursor."""
after: String
"""Returns the elements in the list that come before the specified cursor."""
before: String
"""Returns the first _n_ elements from the list."""
first: Int
"""Returns the last _n_ elements from the list."""
last: Int
"""A query to select and order bugs"""
query: String
): BugConnection!
bug(prefix: String!): Bug
"""All the identities"""
allIdentities(
"""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
): IdentityConnection!
identity(prefix: String!):Identity
"""The identity created or selected by the user as its own"""
userIdentity:Identity
}

View File

@ -1,25 +1,3 @@
scalar Time
scalar Label
scalar Hash
"""Information about pagination in a connection."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!
"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!
"""When paginating backwards, the cursor to continue."""
startCursor: String!
"""When paginating forwards, the cursor to continue."""
endCursor: String!
}
"""An object that has an author."""
interface Authored {
"""The author of this object."""
author: Identity!
}
type Query {
defaultRepository: Repository
repository(id: String!): Repository

View File

@ -0,0 +1,21 @@
scalar Time
scalar Label
scalar Hash
"""Information about pagination in a connection."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!
"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!
"""When paginating backwards, the cursor to continue."""
startCursor: String!
"""When paginating forwards, the cursor to continue."""
endCursor: String!
}
"""An object that has an author."""
interface Authored {
"""The author of this object."""
author: Identity!
}

View File

@ -1,3 +1,3 @@
package graphql
const Version = "dev"
const Version = "v0.7.2"

View File

@ -7,6 +7,7 @@ import (
"io"
"net/http"
"strings"
"time"
"github.com/99designs/gqlgen/complexity"
"github.com/99designs/gqlgen/graphql"
@ -25,15 +26,16 @@ type params struct {
}
type Config struct {
cacheSize int
upgrader websocket.Upgrader
recover graphql.RecoverFunc
errorPresenter graphql.ErrorPresenterFunc
resolverHook graphql.FieldMiddleware
requestHook graphql.RequestMiddleware
tracer graphql.Tracer
complexityLimit int
disableIntrospection bool
cacheSize int
upgrader websocket.Upgrader
recover graphql.RecoverFunc
errorPresenter graphql.ErrorPresenterFunc
resolverHook graphql.FieldMiddleware
requestHook graphql.RequestMiddleware
tracer graphql.Tracer
complexityLimit int
disableIntrospection bool
connectionKeepAlivePingInterval time.Duration
}
func (c *Config) newRequestContext(es graphql.ExecutableSchema, doc *ast.QueryDocument, op *ast.OperationDefinition, query string, variables map[string]interface{}) *graphql.RequestContext {
@ -243,6 +245,14 @@ func CacheSize(size int) Option {
const DefaultCacheSize = 1000
// WebsocketKeepAliveDuration allows you to reconfigure the keepAlive behavior.
// By default, keep-alive is disabled.
func WebsocketKeepAliveDuration(duration time.Duration) Option {
return func(cfg *Config) {
cfg.connectionKeepAlivePingInterval = duration
}
}
func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc {
cfg := &Config{
cacheSize: DefaultCacheSize,

View File

@ -8,6 +8,7 @@ import (
"log"
"net/http"
"sync"
"time"
"github.com/99designs/gqlgen/graphql"
"github.com/gorilla/websocket"
@ -27,7 +28,7 @@ const (
dataMsg = "data" // Server -> Client
errorMsg = "error" // Server -> Client
completeMsg = "complete" // Server -> Client
//connectionKeepAliveMsg = "ka" // Server -> Client TODO: keepalives
connectionKeepAliveMsg = "ka" // Server -> Client
)
type operationMessage struct {
@ -37,12 +38,13 @@ type operationMessage struct {
}
type wsConnection struct {
ctx context.Context
conn *websocket.Conn
exec graphql.ExecutableSchema
active map[string]context.CancelFunc
mu sync.Mutex
cfg *Config
ctx context.Context
conn *websocket.Conn
exec graphql.ExecutableSchema
active map[string]context.CancelFunc
mu sync.Mutex
cfg *Config
keepAliveTicker *time.Ticker
initPayload InitPayload
}
@ -109,6 +111,20 @@ func (c *wsConnection) write(msg *operationMessage) {
}
func (c *wsConnection) run() {
// We create a cancellation that will shutdown the keep-alive when we leave
// this function.
ctx, cancel := context.WithCancel(c.ctx)
defer cancel()
// Create a timer that will fire every interval to keep the connection alive.
if c.cfg.connectionKeepAlivePingInterval != 0 {
c.mu.Lock()
c.keepAliveTicker = time.NewTicker(c.cfg.connectionKeepAlivePingInterval)
c.mu.Unlock()
go c.keepAlive(ctx)
}
for {
message := c.readOp()
if message == nil {
@ -141,6 +157,18 @@ func (c *wsConnection) run() {
}
}
func (c *wsConnection) keepAlive(ctx context.Context) {
for {
select {
case <-ctx.Done():
c.keepAliveTicker.Stop()
return
case <-c.keepAliveTicker.C:
c.write(&operationMessage{Type: connectionKeepAliveMsg})
}
}
}
func (c *wsConnection) subscribe(message *operationMessage) bool {
var reqParams params
if err := jsonDecode(bytes.NewReader(message.Payload), &reqParams); err != nil {