.gitignore đ
@@ -17,3 +17,4 @@ coverage.txt
# nix output directory from `nix build` commands
/result
+webui2
Michael MurĂŠ created
.gitignore | 1
api/graphql/graph/repository.generated.go | 533 +++++++++++++++++++++++++
api/graphql/graph/root.generated.go | 185 ++++++++
api/graphql/graph/root_.generated.go | 93 ++++
api/graphql/models/edges.go | 5
api/graphql/models/gen_models.go | 12
api/graphql/resolvers/query.go | 38 +
api/graphql/schema/repository.graphql | 12
api/graphql/schema/root.graphql | 12
cache/multi_repo_cache.go | 9
go.mod | 17
11 files changed, 906 insertions(+), 11 deletions(-)
@@ -17,3 +17,4 @@ coverage.txt
# nix output directory from `nix build` commands
/result
+webui2
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"strconv"
+ "sync"
"sync/atomic"
"github.com/99designs/gqlgen/graphql"
@@ -853,6 +854,318 @@ func (ec *executionContext) fieldContext_Repository_validLabels(ctx context.Cont
return fc, nil
}
+func (ec *executionContext) _RepositoryConnection_edges(ctx context.Context, field graphql.CollectedField, obj *models.RepositoryConnection) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_RepositoryConnection_edges(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Edges, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.([]*models.RepositoryEdge)
+ fc.Result = res
+ return ec.marshalNRepositoryEdge2áágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryEdgeá(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_RepositoryConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "RepositoryConnection",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "cursor":
+ return ec.fieldContext_RepositoryEdge_cursor(ctx, field)
+ case "node":
+ return ec.fieldContext_RepositoryEdge_node(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type RepositoryEdge", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _RepositoryConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *models.RepositoryConnection) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_RepositoryConnection_nodes(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Nodes, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.([]*models.Repository)
+ fc.Result = res
+ return ec.marshalNRepository2áágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryá(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_RepositoryConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "RepositoryConnection",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "name":
+ return ec.fieldContext_Repository_name(ctx, field)
+ case "allBugs":
+ return ec.fieldContext_Repository_allBugs(ctx, field)
+ case "bug":
+ return ec.fieldContext_Repository_bug(ctx, field)
+ case "allIdentities":
+ return ec.fieldContext_Repository_allIdentities(ctx, field)
+ case "identity":
+ return ec.fieldContext_Repository_identity(ctx, field)
+ case "userIdentity":
+ return ec.fieldContext_Repository_userIdentity(ctx, field)
+ case "validLabels":
+ return ec.fieldContext_Repository_validLabels(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type Repository", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _RepositoryConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.RepositoryConnection) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_RepositoryConnection_pageInfo(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.PageInfo, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.PageInfo)
+ fc.Result = res
+ return ec.marshalNPageInfo2ágithubácomágitábugágitábugáapiágraphqlámodelsáPageInfo(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_RepositoryConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "RepositoryConnection",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "hasNextPage":
+ return ec.fieldContext_PageInfo_hasNextPage(ctx, field)
+ case "hasPreviousPage":
+ return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field)
+ case "startCursor":
+ return ec.fieldContext_PageInfo_startCursor(ctx, field)
+ case "endCursor":
+ return ec.fieldContext_PageInfo_endCursor(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _RepositoryConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.RepositoryConnection) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_RepositoryConnection_totalCount(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.TotalCount, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(int)
+ fc.Result = res
+ return ec.marshalNInt2int(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_RepositoryConnection_totalCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "RepositoryConnection",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Int does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _RepositoryEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *models.RepositoryEdge) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_RepositoryEdge_cursor(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Cursor, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_RepositoryEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "RepositoryEdge",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _RepositoryEdge_node(ctx context.Context, field graphql.CollectedField, obj *models.RepositoryEdge) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_RepositoryEdge_node(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Node, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.Repository)
+ fc.Result = res
+ return ec.marshalNRepository2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepository(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_RepositoryEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "RepositoryEdge",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "name":
+ return ec.fieldContext_Repository_name(ctx, field)
+ case "allBugs":
+ return ec.fieldContext_Repository_allBugs(ctx, field)
+ case "bug":
+ return ec.fieldContext_Repository_bug(ctx, field)
+ case "allIdentities":
+ return ec.fieldContext_Repository_allIdentities(ctx, field)
+ case "identity":
+ return ec.fieldContext_Repository_identity(ctx, field)
+ case "userIdentity":
+ return ec.fieldContext_Repository_userIdentity(ctx, field)
+ case "validLabels":
+ return ec.fieldContext_Repository_validLabels(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type Repository", field.Name)
+ },
+ }
+ return fc, nil
+}
+
// endregion **************************** field.gotpl *****************************
// region **************************** input.gotpl *****************************
@@ -1139,10 +1452,230 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe
return out
}
+var repositoryConnectionImplementors = []string{"RepositoryConnection"}
+
+func (ec *executionContext) _RepositoryConnection(ctx context.Context, sel ast.SelectionSet, obj *models.RepositoryConnection) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, repositoryConnectionImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("RepositoryConnection")
+ case "edges":
+ out.Values[i] = ec._RepositoryConnection_edges(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "nodes":
+ out.Values[i] = ec._RepositoryConnection_nodes(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "pageInfo":
+ out.Values[i] = ec._RepositoryConnection_pageInfo(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "totalCount":
+ out.Values[i] = ec._RepositoryConnection_totalCount(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
+var repositoryEdgeImplementors = []string{"RepositoryEdge"}
+
+func (ec *executionContext) _RepositoryEdge(ctx context.Context, sel ast.SelectionSet, obj *models.RepositoryEdge) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, repositoryEdgeImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("RepositoryEdge")
+ case "cursor":
+ out.Values[i] = ec._RepositoryEdge_cursor(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "node":
+ out.Values[i] = ec._RepositoryEdge_node(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
// endregion **************************** object.gotpl ****************************
// region ***************************** type.gotpl *****************************
+func (ec *executionContext) marshalNRepository2áágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryá(ctx context.Context, sel ast.SelectionSet, v []*models.Repository) graphql.Marshaler {
+ ret := make(graphql.Array, len(v))
+ var wg sync.WaitGroup
+ isLen1 := len(v) == 1
+ if !isLen1 {
+ wg.Add(len(v))
+ }
+ for i := range v {
+ i := i
+ fc := &graphql.FieldContext{
+ Index: &i,
+ Result: &v[i],
+ }
+ ctx := graphql.WithFieldContext(ctx, fc)
+ f := func(i int) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = nil
+ }
+ }()
+ if !isLen1 {
+ defer wg.Done()
+ }
+ ret[i] = ec.marshalNRepository2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepository(ctx, sel, v[i])
+ }
+ if isLen1 {
+ f(i)
+ } else {
+ go f(i)
+ }
+
+ }
+ wg.Wait()
+
+ for _, e := range ret {
+ if e == graphql.Null {
+ return graphql.Null
+ }
+ }
+
+ return ret
+}
+
+func (ec *executionContext) marshalNRepository2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepository(ctx context.Context, sel ast.SelectionSet, v *models.Repository) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ ec.Errorf(ctx, "the requested element is null which the schema does not allow")
+ }
+ return graphql.Null
+ }
+ return ec._Repository(ctx, sel, v)
+}
+
+func (ec *executionContext) marshalNRepositoryConnection2githubácomágitábugágitábugáapiágraphqlámodelsáRepositoryConnection(ctx context.Context, sel ast.SelectionSet, v models.RepositoryConnection) graphql.Marshaler {
+ return ec._RepositoryConnection(ctx, sel, &v)
+}
+
+func (ec *executionContext) marshalNRepositoryConnection2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryConnection(ctx context.Context, sel ast.SelectionSet, v *models.RepositoryConnection) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ ec.Errorf(ctx, "the requested element is null which the schema does not allow")
+ }
+ return graphql.Null
+ }
+ return ec._RepositoryConnection(ctx, sel, v)
+}
+
+func (ec *executionContext) marshalNRepositoryEdge2áágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryEdgeá(ctx context.Context, sel ast.SelectionSet, v []*models.RepositoryEdge) graphql.Marshaler {
+ ret := make(graphql.Array, len(v))
+ var wg sync.WaitGroup
+ isLen1 := len(v) == 1
+ if !isLen1 {
+ wg.Add(len(v))
+ }
+ for i := range v {
+ i := i
+ fc := &graphql.FieldContext{
+ Index: &i,
+ Result: &v[i],
+ }
+ ctx := graphql.WithFieldContext(ctx, fc)
+ f := func(i int) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = nil
+ }
+ }()
+ if !isLen1 {
+ defer wg.Done()
+ }
+ ret[i] = ec.marshalNRepositoryEdge2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryEdge(ctx, sel, v[i])
+ }
+ if isLen1 {
+ f(i)
+ } else {
+ go f(i)
+ }
+
+ }
+ wg.Wait()
+
+ for _, e := range ret {
+ if e == graphql.Null {
+ return graphql.Null
+ }
+ }
+
+ return ret
+}
+
+func (ec *executionContext) marshalNRepositoryEdge2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryEdge(ctx context.Context, sel ast.SelectionSet, v *models.RepositoryEdge) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ ec.Errorf(ctx, "the requested element is null which the schema does not allow")
+ }
+ return graphql.Null
+ }
+ return ec._RepositoryEdge(ctx, sel, v)
+}
+
func (ec *executionContext) marshalORepository2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepository(ctx context.Context, sel ast.SelectionSet, v *models.Repository) graphql.Marshaler {
if v == nil {
return graphql.Null
@@ -29,6 +29,7 @@ type MutationResolver interface {
}
type QueryResolver interface {
Repository(ctx context.Context, ref *string) (*models.Repository, error)
+ Repositories(ctx context.Context, after *string, before *string, first *int, last *int) (*models.RepositoryConnection, error)
}
// endregion ************************** generated!.gotpl **************************
@@ -315,6 +316,103 @@ func (ec *executionContext) field_Query___type_argsName(
return zeroVal, nil
}
+func (ec *executionContext) field_Query_repositories_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
+ var err error
+ args := map[string]any{}
+ arg0, err := ec.field_Query_repositories_argsAfter(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["after"] = arg0
+ arg1, err := ec.field_Query_repositories_argsBefore(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["before"] = arg1
+ arg2, err := ec.field_Query_repositories_argsFirst(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["first"] = arg2
+ arg3, err := ec.field_Query_repositories_argsLast(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["last"] = arg3
+ return args, nil
+}
+func (ec *executionContext) field_Query_repositories_argsAfter(
+ ctx context.Context,
+ rawArgs map[string]any,
+) (*string, error) {
+ if _, ok := rawArgs["after"]; !ok {
+ var zeroVal *string
+ return zeroVal, nil
+ }
+
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after"))
+ if tmp, ok := rawArgs["after"]; ok {
+ return ec.unmarshalOString2ástring(ctx, tmp)
+ }
+
+ var zeroVal *string
+ return zeroVal, nil
+}
+
+func (ec *executionContext) field_Query_repositories_argsBefore(
+ ctx context.Context,
+ rawArgs map[string]any,
+) (*string, error) {
+ if _, ok := rawArgs["before"]; !ok {
+ var zeroVal *string
+ return zeroVal, nil
+ }
+
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before"))
+ if tmp, ok := rawArgs["before"]; ok {
+ return ec.unmarshalOString2ástring(ctx, tmp)
+ }
+
+ var zeroVal *string
+ return zeroVal, nil
+}
+
+func (ec *executionContext) field_Query_repositories_argsFirst(
+ ctx context.Context,
+ rawArgs map[string]any,
+) (*int, error) {
+ if _, ok := rawArgs["first"]; !ok {
+ var zeroVal *int
+ return zeroVal, nil
+ }
+
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first"))
+ if tmp, ok := rawArgs["first"]; ok {
+ return ec.unmarshalOInt2áint(ctx, tmp)
+ }
+
+ var zeroVal *int
+ return zeroVal, nil
+}
+
+func (ec *executionContext) field_Query_repositories_argsLast(
+ ctx context.Context,
+ rawArgs map[string]any,
+) (*int, error) {
+ if _, ok := rawArgs["last"]; !ok {
+ var zeroVal *int
+ return zeroVal, nil
+ }
+
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last"))
+ if tmp, ok := rawArgs["last"]; ok {
+ return ec.unmarshalOInt2áint(ctx, tmp)
+ }
+
+ var zeroVal *int
+ return zeroVal, nil
+}
+
func (ec *executionContext) field_Query_repository_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -992,6 +1090,71 @@ func (ec *executionContext) fieldContext_Query_repository(ctx context.Context, f
return fc, nil
}
+func (ec *executionContext) _Query_repositories(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_Query_repositories(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Query().Repositories(rctx, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int))
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.RepositoryConnection)
+ fc.Result = res
+ return ec.marshalNRepositoryConnection2ágithubácomágitábugágitábugáapiágraphqlámodelsáRepositoryConnection(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Query_repositories(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "edges":
+ return ec.fieldContext_RepositoryConnection_edges(ctx, field)
+ case "nodes":
+ return ec.fieldContext_RepositoryConnection_nodes(ctx, field)
+ case "pageInfo":
+ return ec.fieldContext_RepositoryConnection_pageInfo(ctx, field)
+ case "totalCount":
+ return ec.fieldContext_RepositoryConnection_totalCount(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type RepositoryConnection", field.Name)
+ },
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ err = ec.Recover(ctx, r)
+ ec.Error(ctx, err)
+ }
+ }()
+ ctx = graphql.WithFieldContext(ctx, fc)
+ if fc.Args, err = ec.field_Query_repositories_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+ ec.Error(ctx, err)
+ return fc, err
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query___type(ctx, field)
if err != nil {
@@ -1277,6 +1440,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
}
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
+ case "repositories":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query_repositories(ctx, field)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ rrm := func(ctx context.Context) graphql.Marshaler {
+ return ec.OperationContext.RootResolverMiddleware(ctx,
+ func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ }
+
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
case "__type":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
@@ -375,7 +375,8 @@ type ComplexityRoot struct {
}
Query struct {
- Repository func(childComplexity int, ref *string) int
+ Repositories func(childComplexity int, after *string, before *string, first *int, last *int) int
+ Repository func(childComplexity int, ref *string) int
}
Repository struct {
@@ -388,6 +389,18 @@ type ComplexityRoot struct {
ValidLabels func(childComplexity int, after *string, before *string, first *int, last *int) int
}
+ RepositoryConnection struct {
+ Edges func(childComplexity int) int
+ Nodes func(childComplexity int) int
+ PageInfo func(childComplexity int) int
+ TotalCount func(childComplexity int) int
+ }
+
+ RepositoryEdge struct {
+ Cursor func(childComplexity int) int
+ Node func(childComplexity int) int
+ }
+
Subscription struct {
AllEvents func(childComplexity int, repoRef *string, typename *string) int
BugEvents func(childComplexity int, repoRef *string) int
@@ -1758,6 +1771,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.PageInfo.StartCursor(childComplexity), true
+ case "Query.repositories":
+ if e.complexity.Query.Repositories == nil {
+ break
+ }
+
+ args, err := ec.field_Query_repositories_args(ctx, rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Query.Repositories(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true
+
case "Query.repository":
if e.complexity.Query.Repository == nil {
break
@@ -1844,6 +1869,48 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.Repository.ValidLabels(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true
+ case "RepositoryConnection.edges":
+ if e.complexity.RepositoryConnection.Edges == nil {
+ break
+ }
+
+ return e.complexity.RepositoryConnection.Edges(childComplexity), true
+
+ case "RepositoryConnection.nodes":
+ if e.complexity.RepositoryConnection.Nodes == nil {
+ break
+ }
+
+ return e.complexity.RepositoryConnection.Nodes(childComplexity), true
+
+ case "RepositoryConnection.pageInfo":
+ if e.complexity.RepositoryConnection.PageInfo == nil {
+ break
+ }
+
+ return e.complexity.RepositoryConnection.PageInfo(childComplexity), true
+
+ case "RepositoryConnection.totalCount":
+ if e.complexity.RepositoryConnection.TotalCount == nil {
+ break
+ }
+
+ return e.complexity.RepositoryConnection.TotalCount(childComplexity), true
+
+ case "RepositoryEdge.cursor":
+ if e.complexity.RepositoryEdge.Cursor == nil {
+ break
+ }
+
+ return e.complexity.RepositoryEdge.Cursor(childComplexity), true
+
+ case "RepositoryEdge.node":
+ if e.complexity.RepositoryEdge.Node == nil {
+ break
+ }
+
+ return e.complexity.RepositoryEdge.Node(childComplexity), true
+
case "Subscription.allEvents":
if e.complexity.Subscription.AllEvents == nil {
break
@@ -2679,10 +2746,34 @@ type OperationEdge {
last: Int
): LabelConnection!
}
+
+type RepositoryConnection {
+ edges: [RepositoryEdge!]!
+ nodes: [Repository!]!
+ pageInfo: PageInfo!
+ totalCount: Int!
+}
+
+type RepositoryEdge {
+ cursor: String!
+ node: Repository!
+}
`, BuiltIn: false},
{Name: "../schema/root.graphql", Input: `type Query {
"""Access a repository by reference/name. If no ref is given, the default repository is returned if any."""
repository(ref: String): Repository
+
+ """List all registered repositories."""
+ repositories(
+ """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
+ ): RepositoryConnection!
}
type Mutation # See each entity mutations
@@ -29,3 +29,8 @@ func (e IdentityEdge) GetCursor() string {
func (e LabelEdge) GetCursor() string {
return e.Cursor
}
+
+// GetCursor return the cursor entry of an edge
+func (e RepositoryEdge) GetCursor() string {
+ return e.Cursor
+}
@@ -330,5 +330,17 @@ type PageInfo struct {
type Query struct {
}
+type RepositoryConnection struct {
+ Edges []*RepositoryEdge `json:"edges"`
+ Nodes []*Repository `json:"nodes"`
+ PageInfo *PageInfo `json:"pageInfo"`
+ TotalCount int `json:"totalCount"`
+}
+
+type RepositoryEdge struct {
+ Cursor string `json:"cursor"`
+ Node *Repository `json:"node"`
+}
+
type Subscription struct {
}
@@ -3,6 +3,7 @@ package resolvers
import (
"context"
+ "github.com/git-bug/git-bug/api/graphql/connections"
"github.com/git-bug/git-bug/api/graphql/graph"
"github.com/git-bug/git-bug/api/graphql/models"
"github.com/git-bug/git-bug/cache"
@@ -33,3 +34,40 @@ func (r rootQueryResolver) Repository(_ context.Context, ref *string) (*models.R
Repo: repo,
}, nil
}
+
+// Repositories returns all registered repositories as a relay connection.
+func (r rootQueryResolver) Repositories(_ context.Context, after *string, before *string, first *int, last *int) (*models.RepositoryConnection, error) {
+ input := models.ConnectionInput{
+ After: after,
+ Before: before,
+ First: first,
+ Last: last,
+ }
+
+ source := r.cache.AllRepos()
+
+ edger := func(repo *cache.RepoCache, offset int) connections.Edge {
+ return models.RepositoryEdge{
+ Node: &models.Repository{Cache: r.cache, Repo: repo},
+ Cursor: connections.OffsetToCursor(offset),
+ }
+ }
+
+ // NodeType is *cache.RepoCache (the source slice element), but the connection
+ // nodes field wants []*models.Repository. Extract them from the edges, which
+ // already hold the wrapped Repository built by the edger above.
+ conMaker := func(edges []*models.RepositoryEdge, _ []*cache.RepoCache, info *models.PageInfo, totalCount int) (*models.RepositoryConnection, error) {
+ nodes := make([]*models.Repository, len(edges))
+ for i, e := range edges {
+ nodes[i] = e.Node
+ }
+ return &models.RepositoryConnection{
+ Edges: edges,
+ Nodes: nodes,
+ PageInfo: info,
+ TotalCount: totalCount,
+ }, nil
+ }
+
+ return connections.Connection(source, edger, conMaker, input)
+}
@@ -47,3 +47,15 @@ type Repository {
last: Int
): LabelConnection!
}
+
+type RepositoryConnection {
+ edges: [RepositoryEdge!]!
+ nodes: [Repository!]!
+ pageInfo: PageInfo!
+ totalCount: Int!
+}
+
+type RepositoryEdge {
+ cursor: String!
+ node: Repository!
+}
@@ -1,6 +1,18 @@
type Query {
"""Access a repository by reference/name. If no ref is given, the default repository is returned if any."""
repository(ref: String): Repository
+
+ """List all registered repositories."""
+ repositories(
+ """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
+ ): RepositoryConnection!
}
type Mutation # See each entity mutations
@@ -69,6 +69,15 @@ func (c *MultiRepoCache) ResolveRepo(name string) (*RepoCache, error) {
return r, nil
}
+// AllRepos returns all registered repositories. Order is not guaranteed.
+func (c *MultiRepoCache) AllRepos() []*RepoCache {
+ result := make([]*RepoCache, 0, len(c.repos))
+ for _, r := range c.repos {
+ result = append(result, r)
+ }
+ return result
+}
+
// RegisterObserver registers an Observer on repo and entity, according to nameFilter and typename.
// - if nameFilter is empty, the observer is registered on all available repo
// - if nameFilter is not empty, the observer is registered on the repo with the matching name
@@ -17,6 +17,7 @@ require (
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.16.5
github.com/gorilla/mux v1.8.1
+ github.com/gorilla/websocket v1.5.3
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/icrowley/fake v0.0.0-20240710202011-f797eb4a99c0
github.com/mattn/go-isatty v0.0.20
@@ -32,15 +33,14 @@ require (
github.com/vektah/gqlparser/v2 v2.5.26
gitlab.com/gitlab-org/api/client-go v0.116.0
golang.org/x/crypto v0.45.0
+ golang.org/x/mod v0.29.0
+ golang.org/x/net v0.47.0
golang.org/x/oauth2 v0.27.0
golang.org/x/sync v0.18.0
golang.org/x/sys v0.38.0
+ golang.org/x/term v0.37.0
golang.org/x/text v0.31.0
-)
-
-require (
- golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect
- golang.org/x/tools/godoc v0.1.0-deprecated // indirect
+ golang.org/x/vuln v1.1.3
)
require (
@@ -87,7 +87,6 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gorilla/websocket v1.5.3 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
@@ -114,13 +113,11 @@ require (
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
- golang.org/x/mod v0.29.0
- golang.org/x/net v0.47.0
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect
- golang.org/x/term v0.37.0
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.38.0 // indirect
- golang.org/x/vuln v1.1.3
+ golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect
+ golang.org/x/tools/godoc v0.1.0-deprecated // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect