diff --git a/.gitignore b/.gitignore index d6eae0b3200d3d6c4ce1c648724d07da53309b05..b625a693f232aeb1469e102014c2c245f56c38b2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ coverage.txt # nix output directory from `nix build` commands /result +webui2 diff --git a/api/graphql/graph/repository.generated.go b/api/graphql/graph/repository.generated.go index 050393b3548580a083f439937d18e5f5dcee9f0e..81e39832ce30d106fd54672bbf7e2a362f121fa8 100644 --- a/api/graphql/graph/repository.generated.go +++ b/api/graphql/graph/repository.generated.go @@ -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 diff --git a/api/graphql/graph/root.generated.go b/api/graphql/graph/root.generated.go index 27ac29f8b74298892724b2161576050e27c65605..316430a9ba7553922d842588ae45158c2a257e6c 100644 --- a/api/graphql/graph/root.generated.go +++ b/api/graphql/graph/root.generated.go @@ -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) { diff --git a/api/graphql/graph/root_.generated.go b/api/graphql/graph/root_.generated.go index 5fed0daf75c51de15f387319ebd8724ceddf0d91..14bbfc1a67c473b8e749c632bf0ab61e3eeeaa6e 100644 --- a/api/graphql/graph/root_.generated.go +++ b/api/graphql/graph/root_.generated.go @@ -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 diff --git a/api/graphql/models/edges.go b/api/graphql/models/edges.go index 95509970912a93dbea8f6494e7c58799559a8ad9..f746e66971db35d94d689bcd1a7e1ec591906a2d 100644 --- a/api/graphql/models/edges.go +++ b/api/graphql/models/edges.go @@ -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 +} diff --git a/api/graphql/models/gen_models.go b/api/graphql/models/gen_models.go index 5a9affaa72d30449118e6fffb2f5d57e75bde9c1..c7694a03daf808609a5351af947a8f2797f34eea 100644 --- a/api/graphql/models/gen_models.go +++ b/api/graphql/models/gen_models.go @@ -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 { } diff --git a/api/graphql/resolvers/query.go b/api/graphql/resolvers/query.go index b08d98251ff9b5ea3e03073c45619df78b45893f..98153993b22ed341895a1c5b758d33f5551aab60 100644 --- a/api/graphql/resolvers/query.go +++ b/api/graphql/resolvers/query.go @@ -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) +} diff --git a/api/graphql/schema/repository.graphql b/api/graphql/schema/repository.graphql index ccd409f54e829ddf0a7e6f076d05f0ba80d5ae3e..6eea58f6e6998a8474ec6011261e186305d97997 100644 --- a/api/graphql/schema/repository.graphql +++ b/api/graphql/schema/repository.graphql @@ -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! +} diff --git a/api/graphql/schema/root.graphql b/api/graphql/schema/root.graphql index 721698b0d0ebae818168a36de10f073e08568968..388ab9cef15beb67562e9a6747782ace919ddfa4 100644 --- a/api/graphql/schema/root.graphql +++ b/api/graphql/schema/root.graphql @@ -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 diff --git a/cache/multi_repo_cache.go b/cache/multi_repo_cache.go index 333fb34008c94d44624126b4f526518864ee6765..40d542f516423c1984915d588927acf5010bb7a7 100644 --- a/cache/multi_repo_cache.go +++ b/cache/multi_repo_cache.go @@ -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 diff --git a/go.mod b/go.mod index 2a7df6f9e5b0326fab9a4b9bb13f9bb17dd95db4..0c4c9c103460306cf3ba1e288681441e22a4c9da 100644 --- a/go.mod +++ b/go.mod @@ -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