Detailed changes
@@ -9,6 +9,7 @@ model:
autobind:
- "github.com/git-bug/git-bug/api/graphql/models"
+ - "github.com/git-bug/git-bug/cache"
- "github.com/git-bug/git-bug/repository"
- "github.com/git-bug/git-bug/entity"
- "github.com/git-bug/git-bug/entities/common"
@@ -107,9 +107,9 @@ func (ec *executionContext) _OperationConnection_nodes(ctx context.Context, fiel
}
return graphql.Null
}
- res := resTmp.([]dag.OperationWithApply[*bug.Snapshot])
+ res := resTmp.([]dag.Operation)
fc.Result = res
- return ec.marshalNOperation2ágithubácomágitábugágitábugáentityádagáOperationWithApplyá(ctx, field.Selections, res)
+ return ec.marshalNOperation2ágithubácomágitábugágitábugáentityádagáOperationá(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_OperationConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@@ -293,9 +293,9 @@ func (ec *executionContext) _OperationEdge_node(ctx context.Context, field graph
}
return graphql.Null
}
- res := resTmp.(dag.OperationWithApply[*bug.Snapshot])
+ res := resTmp.(dag.Operation)
fc.Result = res
- return ec.marshalNOperation2githubácomágitábugágitábugáentityádagáOperationWithApply(ctx, field.Selections, res)
+ return ec.marshalNOperation2githubácomágitábugágitábugáentityádagáOperation(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_OperationEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@@ -319,7 +319,7 @@ func (ec *executionContext) fieldContext_OperationEdge_node(_ context.Context, f
// region ************************** interface.gotpl ***************************
-func (ec *executionContext) _Operation(ctx context.Context, sel ast.SelectionSet, obj dag.OperationWithApply[*bug.Snapshot]) graphql.Marshaler {
+func (ec *executionContext) _Operation(ctx context.Context, sel ast.SelectionSet, obj dag.Operation) graphql.Marshaler {
switch obj := (obj).(type) {
case nil:
return graphql.Null
@@ -464,7 +464,7 @@ func (ec *executionContext) _OperationEdge(ctx context.Context, sel ast.Selectio
// region ***************************** type.gotpl *****************************
-func (ec *executionContext) marshalNOperation2githubácomágitábugágitábugáentityádagáOperationWithApply(ctx context.Context, sel ast.SelectionSet, v dag.OperationWithApply[*bug.Snapshot]) graphql.Marshaler {
+func (ec *executionContext) marshalNOperation2githubácomágitábugágitábugáentityádagáOperation(ctx context.Context, sel ast.SelectionSet, v dag.Operation) 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")
@@ -474,7 +474,7 @@ func (ec *executionContext) marshalNOperation2githubácomágitábugágitáb
return ec._Operation(ctx, sel, v)
}
-func (ec *executionContext) marshalNOperation2ágithubácomágitábugágitábugáentityádagáOperationWithApplyá(ctx context.Context, sel ast.SelectionSet, v []dag.OperationWithApply[*bug.Snapshot]) graphql.Marshaler {
+func (ec *executionContext) marshalNOperation2ágithubácomágitábugágitábugáentityádagáOperationá(ctx context.Context, sel ast.SelectionSet, v []dag.Operation) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
@@ -498,7 +498,7 @@ func (ec *executionContext) marshalNOperation2ágithubácomágitábugágit
if !isLen1 {
defer wg.Done()
}
- ret[i] = ec.marshalNOperation2githubácomágitábugágitábugáentityádagáOperationWithApply(ctx, sel, v[i])
+ ret[i] = ec.marshalNOperation2githubácomágitábugágitábugáentityádagáOperation(ctx, sel, v[i])
}
if isLen1 {
f(i)
@@ -389,9 +389,9 @@ type ComplexityRoot struct {
}
Subscription struct {
- AllEvents func(childComplexity int, repoFilter *string) int
- BugEvents func(childComplexity int, repoFilter *string, query *string) int
- IdentityEvents func(childComplexity int, repoFilter *string) int
+ AllEvents func(childComplexity int, repoRef *string, typename *string) int
+ BugEvents func(childComplexity int, repoRef *string) int
+ IdentityEvents func(childComplexity int, repoRef *string) int
}
}
@@ -1854,7 +1854,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
- return e.complexity.Subscription.AllEvents(childComplexity, args["repoFilter"].(*string)), true
+ return e.complexity.Subscription.AllEvents(childComplexity, args["repoRef"].(*string), args["typename"].(*string)), true
case "Subscription.bugEvents":
if e.complexity.Subscription.BugEvents == nil {
@@ -1866,7 +1866,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
- return e.complexity.Subscription.BugEvents(childComplexity, args["repoFilter"].(*string), args["query"].(*string)), true
+ return e.complexity.Subscription.BugEvents(childComplexity, args["repoRef"].(*string)), true
case "Subscription.identityEvents":
if e.complexity.Subscription.IdentityEvents == nil {
@@ -1878,7 +1878,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
- return e.complexity.Subscription.IdentityEvents(childComplexity, args["repoFilter"].(*string)), true
+ return e.complexity.Subscription.IdentityEvents(childComplexity, args["repoRef"].(*string)), true
}
return 0, false
@@ -2604,7 +2604,8 @@ type LabelChangeResult {
}
`, BuiltIn: false},
{Name: "../schema/operation.graphql", Input: `"""An operation applied to an entity."""
-interface Operation {
+interface Operation
+@goModel(model: "github.com/git-bug/git-bug/entity/dag.Operation") {
"""The identifier of the operation"""
id: ID!
"""The operations author."""
@@ -2693,31 +2694,31 @@ type Mutation # See each entity mutations
`, BuiltIn: false},
{Name: "../schema/subscription.graphql", Input: `type Subscription {
"""Subscribe to events on all entities. For events on a specific repo you can provide a repo reference. Without it, you get the unique default repo or all repo events."""
- allEvents(repoFilter: String): EntityEvent!
+ allEvents(repoRef: String, typename: String): EntityEvent!
"""Subscribe to identity entity events. For events on a specific repo you can provide a repo reference. Without it, you get the unique default repo or all repo events."""
- identityEvents(repoFilter: String): IdentityEvent!
+ identityEvents(repoRef: String): IdentityEvent!
"""Subscribe to bug entity events. For events on a specific repo you can provide a repo reference. Without it, you get the unique default repo or all repo events."""
- bugEvents(repoFilter: String, query: String): BugEvent!
+ bugEvents(repoRef: String): BugEvent!
}
-enum EventType {
+enum EntityEventType {
CREATED
UPDATED
REMOVED
}
type EntityEvent {
- type: EventType!
+ type: EntityEventType!
entity: Entity
}
type IdentityEvent {
- type: EventType!
+ type: EntityEventType!
identity: Identity!
}
type BugEvent {
- type: EventType!
+ type: EntityEventType!
bug: Bug!
}
`, BuiltIn: false},
@@ -12,15 +12,16 @@ import (
"github.com/99designs/gqlgen/graphql"
"github.com/git-bug/git-bug/api/graphql/models"
+ "github.com/git-bug/git-bug/cache"
"github.com/vektah/gqlparser/v2/ast"
)
// region ************************** generated!.gotpl **************************
type SubscriptionResolver interface {
- AllEvents(ctx context.Context, repoFilter *string) (<-chan *models.EntityEvent, error)
- IdentityEvents(ctx context.Context, repoFilter *string) (<-chan *models.IdentityEvent, error)
- BugEvents(ctx context.Context, repoFilter *string, query *string) (<-chan *models.BugEvent, error)
+ AllEvents(ctx context.Context, repoRef *string, typename *string) (<-chan *models.EntityEvent, error)
+ IdentityEvents(ctx context.Context, repoRef *string) (<-chan *models.IdentityEvent, error)
+ BugEvents(ctx context.Context, repoRef *string) (<-chan *models.BugEvent, error)
}
// endregion ************************** generated!.gotpl **************************
@@ -30,24 +31,29 @@ type SubscriptionResolver interface {
func (ec *executionContext) field_Subscription_allEvents_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
- arg0, err := ec.field_Subscription_allEvents_argsRepoFilter(ctx, rawArgs)
+ arg0, err := ec.field_Subscription_allEvents_argsRepoRef(ctx, rawArgs)
if err != nil {
return nil, err
}
- args["repoFilter"] = arg0
+ args["repoRef"] = arg0
+ arg1, err := ec.field_Subscription_allEvents_argsTypename(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["typename"] = arg1
return args, nil
}
-func (ec *executionContext) field_Subscription_allEvents_argsRepoFilter(
+func (ec *executionContext) field_Subscription_allEvents_argsRepoRef(
ctx context.Context,
rawArgs map[string]any,
) (*string, error) {
- if _, ok := rawArgs["repoFilter"]; !ok {
+ if _, ok := rawArgs["repoRef"]; !ok {
var zeroVal *string
return zeroVal, nil
}
- ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("repoFilter"))
- if tmp, ok := rawArgs["repoFilter"]; ok {
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("repoRef"))
+ if tmp, ok := rawArgs["repoRef"]; ok {
return ec.unmarshalOString2ástring(ctx, tmp)
}
@@ -55,32 +61,17 @@ func (ec *executionContext) field_Subscription_allEvents_argsRepoFilter(
return zeroVal, nil
}
-func (ec *executionContext) field_Subscription_bugEvents_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
- var err error
- args := map[string]any{}
- arg0, err := ec.field_Subscription_bugEvents_argsRepoFilter(ctx, rawArgs)
- if err != nil {
- return nil, err
- }
- args["repoFilter"] = arg0
- arg1, err := ec.field_Subscription_bugEvents_argsQuery(ctx, rawArgs)
- if err != nil {
- return nil, err
- }
- args["query"] = arg1
- return args, nil
-}
-func (ec *executionContext) field_Subscription_bugEvents_argsRepoFilter(
+func (ec *executionContext) field_Subscription_allEvents_argsTypename(
ctx context.Context,
rawArgs map[string]any,
) (*string, error) {
- if _, ok := rawArgs["repoFilter"]; !ok {
+ if _, ok := rawArgs["typename"]; !ok {
var zeroVal *string
return zeroVal, nil
}
- ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("repoFilter"))
- if tmp, ok := rawArgs["repoFilter"]; ok {
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("typename"))
+ if tmp, ok := rawArgs["typename"]; ok {
return ec.unmarshalOString2ástring(ctx, tmp)
}
@@ -88,17 +79,27 @@ func (ec *executionContext) field_Subscription_bugEvents_argsRepoFilter(
return zeroVal, nil
}
-func (ec *executionContext) field_Subscription_bugEvents_argsQuery(
+func (ec *executionContext) field_Subscription_bugEvents_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
+ var err error
+ args := map[string]any{}
+ arg0, err := ec.field_Subscription_bugEvents_argsRepoRef(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["repoRef"] = arg0
+ return args, nil
+}
+func (ec *executionContext) field_Subscription_bugEvents_argsRepoRef(
ctx context.Context,
rawArgs map[string]any,
) (*string, error) {
- if _, ok := rawArgs["query"]; !ok {
+ if _, ok := rawArgs["repoRef"]; !ok {
var zeroVal *string
return zeroVal, nil
}
- ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("query"))
- if tmp, ok := rawArgs["query"]; ok {
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("repoRef"))
+ if tmp, ok := rawArgs["repoRef"]; ok {
return ec.unmarshalOString2ástring(ctx, tmp)
}
@@ -109,24 +110,24 @@ func (ec *executionContext) field_Subscription_bugEvents_argsQuery(
func (ec *executionContext) field_Subscription_identityEvents_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
- arg0, err := ec.field_Subscription_identityEvents_argsRepoFilter(ctx, rawArgs)
+ arg0, err := ec.field_Subscription_identityEvents_argsRepoRef(ctx, rawArgs)
if err != nil {
return nil, err
}
- args["repoFilter"] = arg0
+ args["repoRef"] = arg0
return args, nil
}
-func (ec *executionContext) field_Subscription_identityEvents_argsRepoFilter(
+func (ec *executionContext) field_Subscription_identityEvents_argsRepoRef(
ctx context.Context,
rawArgs map[string]any,
) (*string, error) {
- if _, ok := rawArgs["repoFilter"]; !ok {
+ if _, ok := rawArgs["repoRef"]; !ok {
var zeroVal *string
return zeroVal, nil
}
- ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("repoFilter"))
- if tmp, ok := rawArgs["repoFilter"]; ok {
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("repoRef"))
+ if tmp, ok := rawArgs["repoRef"]; ok {
return ec.unmarshalOString2ástring(ctx, tmp)
}
@@ -168,9 +169,9 @@ func (ec *executionContext) _BugEvent_type(ctx context.Context, field graphql.Co
}
return graphql.Null
}
- res := resTmp.(models.EventType)
+ res := resTmp.(cache.EntityEventType)
fc.Result = res
- return ec.marshalNEventType2githubácomágitábugágitábugáapiágraphqlámodelsáEventType(ctx, field.Selections, res)
+ return ec.marshalNEntityEventType2githubácomágitábugágitábugácacheáEntityEventType(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_BugEvent_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@@ -180,7 +181,7 @@ func (ec *executionContext) fieldContext_BugEvent_type(_ context.Context, field
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
- return nil, errors.New("field of type EventType does not have child fields")
+ return nil, errors.New("field of type EntityEventType does not have child fields")
},
}
return fc, nil
@@ -284,9 +285,9 @@ func (ec *executionContext) _EntityEvent_type(ctx context.Context, field graphql
}
return graphql.Null
}
- res := resTmp.(models.EventType)
+ res := resTmp.(cache.EntityEventType)
fc.Result = res
- return ec.marshalNEventType2githubácomágitábugágitábugáapiágraphqlámodelsáEventType(ctx, field.Selections, res)
+ return ec.marshalNEntityEventType2githubácomágitábugágitábugácacheáEntityEventType(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_EntityEvent_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@@ -296,7 +297,7 @@ func (ec *executionContext) fieldContext_EntityEvent_type(_ context.Context, fie
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
- return nil, errors.New("field of type EventType does not have child fields")
+ return nil, errors.New("field of type EntityEventType does not have child fields")
},
}
return fc, nil
@@ -369,9 +370,9 @@ func (ec *executionContext) _IdentityEvent_type(ctx context.Context, field graph
}
return graphql.Null
}
- res := resTmp.(models.EventType)
+ res := resTmp.(cache.EntityEventType)
fc.Result = res
- return ec.marshalNEventType2githubácomágitábugágitábugáapiágraphqlámodelsáEventType(ctx, field.Selections, res)
+ return ec.marshalNEntityEventType2githubácomágitábugágitábugácacheáEntityEventType(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_IdentityEvent_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@@ -381,7 +382,7 @@ func (ec *executionContext) fieldContext_IdentityEvent_type(_ context.Context, f
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
- return nil, errors.New("field of type EventType does not have child fields")
+ return nil, errors.New("field of type EntityEventType does not have child fields")
},
}
return fc, nil
@@ -463,7 +464,7 @@ func (ec *executionContext) _Subscription_allEvents(ctx context.Context, field g
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
- return ec.resolvers.Subscription().AllEvents(rctx, fc.Args["repoFilter"].(*string))
+ return ec.resolvers.Subscription().AllEvents(rctx, fc.Args["repoRef"].(*string), fc.Args["typename"].(*string))
})
if err != nil {
ec.Error(ctx, err)
@@ -538,7 +539,7 @@ func (ec *executionContext) _Subscription_identityEvents(ctx context.Context, fi
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
- return ec.resolvers.Subscription().IdentityEvents(rctx, fc.Args["repoFilter"].(*string))
+ return ec.resolvers.Subscription().IdentityEvents(rctx, fc.Args["repoRef"].(*string))
})
if err != nil {
ec.Error(ctx, err)
@@ -613,7 +614,7 @@ func (ec *executionContext) _Subscription_bugEvents(ctx context.Context, field g
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
- return ec.resolvers.Subscription().BugEvents(rctx, fc.Args["repoFilter"].(*string), fc.Args["query"].(*string))
+ return ec.resolvers.Subscription().BugEvents(rctx, fc.Args["repoRef"].(*string))
})
if err != nil {
ec.Error(ctx, err)
@@ -871,13 +872,13 @@ func (ec *executionContext) marshalNEntityEvent2ágithubácomágitábugági
return ec._EntityEvent(ctx, sel, v)
}
-func (ec *executionContext) unmarshalNEventType2githubácomágitábugágitábugáapiágraphqlámodelsáEventType(ctx context.Context, v any) (models.EventType, error) {
- var res models.EventType
+func (ec *executionContext) unmarshalNEntityEventType2githubácomágitábugágitábugácacheáEntityEventType(ctx context.Context, v any) (cache.EntityEventType, error) {
+ var res cache.EntityEventType
err := res.UnmarshalGQL(v)
return res, graphql.ErrorOnPath(ctx, err)
}
-func (ec *executionContext) marshalNEventType2githubácomágitábugágitábugáapiágraphqlámodelsáEventType(ctx context.Context, sel ast.SelectionSet, v models.EventType) graphql.Marshaler {
+func (ec *executionContext) marshalNEntityEventType2githubácomágitábugágitábugácacheáEntityEventType(ctx context.Context, sel ast.SelectionSet, v cache.EntityEventType) graphql.Marshaler {
return v
}
@@ -3,11 +3,7 @@
package models
import (
- "bytes"
- "fmt"
- "io"
- "strconv"
-
+ "github.com/git-bug/git-bug/cache"
"github.com/git-bug/git-bug/entities/bug"
"github.com/git-bug/git-bug/entities/common"
"github.com/git-bug/git-bug/entity/dag"
@@ -194,8 +190,8 @@ type BugEditCommentPayload struct {
}
type BugEvent struct {
- Type EventType `json:"type"`
- Bug BugWrapper `json:"bug"`
+ Type cache.EntityEventType `json:"type"`
+ Bug BugWrapper `json:"bug"`
}
type BugSetTitleInput struct {
@@ -269,8 +265,8 @@ type BugTimelineItemEdge struct {
}
type EntityEvent struct {
- Type EventType `json:"type"`
- Entity Entity `json:"entity,omitempty"`
+ Type cache.EntityEventType `json:"type"`
+ Entity Entity `json:"entity,omitempty"`
}
type IdentityConnection struct {
@@ -286,8 +282,8 @@ type IdentityEdge struct {
}
type IdentityEvent struct {
- Type EventType `json:"type"`
- Identity IdentityWrapper `json:"identity"`
+ Type cache.EntityEventType `json:"type"`
+ Identity IdentityWrapper `json:"identity"`
}
type LabelConnection struct {
@@ -307,16 +303,16 @@ type Mutation struct {
// The connection type for an Operation
type OperationConnection struct {
- Edges []*OperationEdge `json:"edges"`
- Nodes []dag.OperationWithApply[*bug.Snapshot] `json:"nodes"`
- PageInfo *PageInfo `json:"pageInfo"`
- TotalCount int `json:"totalCount"`
+ Edges []*OperationEdge `json:"edges"`
+ Nodes []dag.Operation `json:"nodes"`
+ PageInfo *PageInfo `json:"pageInfo"`
+ TotalCount int `json:"totalCount"`
}
// Represent an Operation
type OperationEdge struct {
- Cursor string `json:"cursor"`
- Node dag.OperationWithApply[*bug.Snapshot] `json:"node"`
+ Cursor string `json:"cursor"`
+ Node dag.Operation `json:"node"`
}
// Information about pagination in a connection.
@@ -336,60 +332,3 @@ type Query struct {
type Subscription struct {
}
-
-type EventType string
-
-const (
- EventTypeCreated EventType = "CREATED"
- EventTypeUpdated EventType = "UPDATED"
- EventTypeRemoved EventType = "REMOVED"
-)
-
-var AllEventType = []EventType{
- EventTypeCreated,
- EventTypeUpdated,
- EventTypeRemoved,
-}
-
-func (e EventType) IsValid() bool {
- switch e {
- case EventTypeCreated, EventTypeUpdated, EventTypeRemoved:
- return true
- }
- return false
-}
-
-func (e EventType) String() string {
- return string(e)
-}
-
-func (e *EventType) UnmarshalGQL(v any) error {
- str, ok := v.(string)
- if !ok {
- return fmt.Errorf("enums must be strings")
- }
-
- *e = EventType(str)
- if !e.IsValid() {
- return fmt.Errorf("%s is not a valid EventType", str)
- }
- return nil
-}
-
-func (e EventType) MarshalGQL(w io.Writer) {
- fmt.Fprint(w, strconv.Quote(e.String()))
-}
-
-func (e *EventType) UnmarshalJSON(b []byte) error {
- s, err := strconv.Unquote(string(b))
- if err != nil {
- return err
- }
- return e.UnmarshalGQL(s)
-}
-
-func (e EventType) MarshalJSON() ([]byte, error) {
- var buf bytes.Buffer
- e.MarshalGQL(&buf)
- return buf.Bytes(), nil
-}
@@ -8,6 +8,7 @@ import (
"github.com/git-bug/git-bug/api/graphql/models"
"github.com/git-bug/git-bug/cache"
"github.com/git-bug/git-bug/entities/bug"
+ "github.com/git-bug/git-bug/entities/identity"
"github.com/git-bug/git-bug/entity"
)
@@ -17,91 +18,128 @@ type subscriptionResolver struct {
cache *cache.MultiRepoCache
}
-func (s subscriptionResolver) AllEvents(ctx context.Context, repoFilter *string) (<-chan *models.EntityEvent, error) {
- // TODO implement me
- panic("implement me")
-}
+func (s subscriptionResolver) AllEvents(ctx context.Context, repoRef *string, typename *string) (<-chan *models.EntityEvent, error) {
+ out := make(chan *models.EntityEvent)
+ sub := &subscription[models.EntityEvent]{
+ cache: s.cache,
+ out: out,
+ makeEvent: func(repo *cache.RepoCache, excerpt cache.Excerpt, eventType cache.EntityEventType) *models.EntityEvent {
+ switch excerpt := excerpt.(type) {
+ case *cache.BugExcerpt:
+ return &models.EntityEvent{Type: eventType, Entity: models.NewLazyBug(repo, excerpt)}
+ case *cache.IdentityExcerpt:
+ return &models.EntityEvent{Type: eventType, Entity: models.NewLazyIdentity(repo, excerpt)}
+ default:
+ panic(fmt.Sprintf("unknown excerpt type: %T", excerpt))
+ }
+ },
+ }
-var _ cache.Observer = &subscription[any]{}
+ var repoRefStr string
+ if repoRef != nil {
+ repoRefStr = *repoRef
+ }
-type subscription[T any] struct {
- out chan *T
- filter func(T) bool
- excerptResolver func(id entity.Id) cache.Excerpt
- repo *cache.RepoCache
-}
+ var typenameStr string
+ if typename != nil {
+ typenameStr = *typename
+ }
-func (s subscription[T]) EntityEvent(event cache.EntityEventType, typename string, id entity.Id) {
+ err := s.cache.RegisterObserver(sub, repoRefStr, typenameStr)
+ if err != nil {
+ return nil, err
+ }
+ go func() {
+ <-ctx.Done()
+ s.cache.UnregisterObserver(sub)
+ }()
+
+ return out, nil
}
-func (s subscriptionResolver) IdentityEvents(ctx context.Context, repoFilter *string) (<-chan *models.IdentityEvent, error) {
- out := make(chan *models.IdentityEvent)
- sub := &subscription[models.IdentityEvent]{
- out: out,
- filter: func(e models.IdentityEvent) bool { return true},
- excerptResolver: s.cache.
+func (s subscriptionResolver) BugEvents(ctx context.Context, repoRef *string) (<-chan *models.BugEvent, error) {
+ out := make(chan *models.BugEvent)
+ sub := &subscription[models.BugEvent]{
+ cache: s.cache,
+ out: out,
+ makeEvent: func(repo *cache.RepoCache, excerpt cache.Excerpt, event cache.EntityEventType) *models.BugEvent {
+ return &models.BugEvent{Type: event, Bug: models.NewLazyBug(repo, excerpt.(*cache.BugExcerpt))}
+ },
+ }
+
+ var repoRefStr string
+ if repoRef != nil {
+ repoRefStr = *repoRef
}
-}
-func (s subscriptionResolver) BugEvents(ctx context.Context, repoFilter *string, query *string) (<-chan *models.BugEvent, error) {
- // TODO implement me
- panic("implement me")
+ err := s.cache.RegisterObserver(sub, repoRefStr, bug.Typename)
+ if err != nil {
+ return nil, err
+ }
+
+ go func() {
+ <-ctx.Done()
+ s.cache.UnregisterObserver(sub)
+ }()
+
+ return out, nil
}
-func (s subscriptionResolver) BugChanged(ctx context.Context, repoRef *string, query *string) (<-chan *models.BugChange, error) {
- var repo *cache.RepoCache
- var err error
+func (s subscriptionResolver) IdentityEvents(ctx context.Context, repoRef *string) (<-chan *models.IdentityEvent, error) {
+ out := make(chan *models.IdentityEvent)
+ sub := &subscription[models.IdentityEvent]{
+ cache: s.cache,
+ out: out,
+ makeEvent: func(repo *cache.RepoCache, excerpt cache.Excerpt, event cache.EntityEventType) *models.IdentityEvent {
+ return &models.IdentityEvent{Type: event, Identity: models.NewLazyIdentity(repo, excerpt.(*cache.IdentityExcerpt))}
+ },
+ }
- if repoRef == nil {
- repo, err = s.cache.DefaultRepo()
- } else {
- repo, err = s.cache.ResolveRepo(*repoRef)
+ var repoRefStr string
+ if repoRef != nil {
+ repoRefStr = *repoRef
}
+ err := s.cache.RegisterObserver(sub, repoRefStr, identity.Typename)
if err != nil {
return nil, err
}
- out := make(chan *models.BugChange)
- sub := bugSubscription{out: out, repo: repo}
- repo.RegisterObserver(bug.Typename, sub)
-
go func() {
<-ctx.Done()
- repo.RegisterObserver(bug.Typename, sub)
+ s.cache.UnregisterObserver(sub)
}()
return out, nil
}
-type bugSubscription struct {
- out chan *models.BugChange
- repo *cache.RepoCache
+var _ cache.Observer = &subscription[any]{}
+
+type subscription[eventT any] struct {
+ cache *cache.MultiRepoCache
+ out chan *eventT
+ filter func(cache.Excerpt) bool
+ makeEvent func(repo *cache.RepoCache, excerpt cache.Excerpt, event cache.EntityEventType) *eventT
}
-func (bs bugSubscription) EntityCreated(_ string, id entity.Id) {
- excerpt, err := bs.repo.Bugs().ResolveExcerpt(id)
+func (s subscription[eventT]) EntityEvent(event cache.EntityEventType, repoName string, typename string, id entity.Id) {
+ repo, err := s.cache.ResolveRepo(repoName)
if err != nil {
- // Should never happen
- fmt.Printf("bug in the cache: could not resolve excerpt for %s: %s\n", id, err)
+ // something terrible happened
return
}
- bs.out <- &models.BugChange{
- Type: models.ChangeTypeCreated,
- Bug: models.NewLazyBug(bs.repo, excerpt),
+ var excerpt cache.Excerpt
+ switch typename {
+ case bug.Typename:
+ excerpt, err = repo.Bugs().ResolveExcerpt(id)
+ case identity.Typename:
+ excerpt, err = repo.Identities().ResolveExcerpt(id)
+ default:
+ panic(fmt.Sprintf("unknown typename: %s", typename))
}
-}
-
-func (bs bugSubscription) EntityUpdated(_ string, id entity.Id) {
- excerpt, err := bs.repo.Bugs().ResolveExcerpt(id)
- if err != nil {
- // Should never happen
- fmt.Printf("bug in the cache: could not resolve excerpt for %s: %s\n", id, err)
+ if s.filter != nil && !s.filter(excerpt) {
return
}
- bs.out <- &models.BugChange{
- Type: models.ChangeTypeUpdated,
- Bug: models.NewLazyBug(bs.repo, excerpt),
- }
+ s.out <- s.makeEvent(repo, excerpt, event)
}
@@ -1,5 +1,6 @@
"""An operation applied to an entity."""
-interface Operation {
+interface Operation
+@goModel(model: "github.com/git-bug/git-bug/entity/dag.Operation") {
"""The identifier of the operation"""
id: ID!
"""The operations author."""
@@ -1,29 +1,29 @@
type Subscription {
"""Subscribe to events on all entities. For events on a specific repo you can provide a repo reference. Without it, you get the unique default repo or all repo events."""
- allEvents(repoFilter: String): EntityEvent!
+ allEvents(repoRef: String, typename: String): EntityEvent!
"""Subscribe to identity entity events. For events on a specific repo you can provide a repo reference. Without it, you get the unique default repo or all repo events."""
- identityEvents(repoFilter: String): IdentityEvent!
+ identityEvents(repoRef: String): IdentityEvent!
"""Subscribe to bug entity events. For events on a specific repo you can provide a repo reference. Without it, you get the unique default repo or all repo events."""
- bugEvents(repoFilter: String, query: String): BugEvent!
+ bugEvents(repoRef: String): BugEvent!
}
-enum EventType {
+enum EntityEventType {
CREATED
UPDATED
REMOVED
}
type EntityEvent {
- type: EventType!
+ type: EntityEventType!
entity: Entity
}
type IdentityEvent {
- type: EventType!
+ type: EntityEventType!
identity: Identity!
}
type BugEvent {
- type: EventType!
+ type: EntityEventType!
bug: Bug!
}
@@ -1,6 +1,12 @@
package cache
-import "github.com/git-bug/git-bug/entity"
+import (
+ "fmt"
+ "io"
+ "strconv"
+
+ "github.com/git-bug/git-bug/entity"
+)
type BuildEventType int
@@ -43,5 +49,36 @@ const (
// Observer gets notified of changes in entities in the cache
type Observer interface {
- EntityEvent(event EntityEventType, repoRef string, typename string, id entity.Id)
+ EntityEvent(event EntityEventType, repoName string, typename string, id entity.Id)
+}
+
+func (e EntityEventType) MarshalGQL(w io.Writer) {
+ switch e {
+ case EntityEventCreated:
+ _, _ = w.Write([]byte(strconv.Quote("CREATED")))
+ case EntityEventUpdated:
+ _, _ = w.Write([]byte(strconv.Quote("UPDATED")))
+ case EntityEventRemoved:
+ _, _ = w.Write([]byte(strconv.Quote("REMOVED")))
+ default:
+ panic("missing case")
+ }
+}
+
+func (e *EntityEventType) UnmarshalGQL(v interface{}) error {
+ str, ok := v.(string)
+ if !ok {
+ return fmt.Errorf("enums must be strings")
+ }
+ switch str {
+ case "CREATED":
+ *e = EntityEventCreated
+ case "UPDATED":
+ *e = EntityEventUpdated
+ case "REMOVED":
+ *e = EntityEventRemoved
+ default:
+ return fmt.Errorf("%s is not a valid EntityEventType", str)
+ }
+ return nil
}
@@ -20,7 +20,7 @@ func NewMultiRepoCache() *MultiRepoCache {
}
}
-// RegisterRepository register a named repository. Use this for multi-repo setup
+// RegisterRepository registers a named repository. Use this for multi-repo setup
func (c *MultiRepoCache) RegisterRepository(repo repository.ClockedRepo, name string) (*RepoCache, chan BuildEvent) {
r, events := NewNamedRepoCache(repo, name)
@@ -42,12 +42,12 @@ func (c *MultiRepoCache) RegisterRepository(repo repository.ClockedRepo, name st
return r, out
}
-// RegisterDefaultRepository register an unnamed repository. Use this for single-repo setup
+// RegisterDefaultRepository registers an unnamed repository. Use this for single-repo setup
func (c *MultiRepoCache) RegisterDefaultRepository(repo repository.ClockedRepo) (*RepoCache, chan BuildEvent) {
return c.RegisterRepository(repo, defaultRepoName)
}
-// DefaultRepo retrieve the default repository
+// DefaultRepo retrieves the default repository
func (c *MultiRepoCache) DefaultRepo() (*RepoCache, error) {
if len(c.repos) != 1 {
return nil, fmt.Errorf("repository is not unique")
@@ -60,7 +60,7 @@ func (c *MultiRepoCache) DefaultRepo() (*RepoCache, error) {
panic("unreachable")
}
-// ResolveRepo retrieve a repository by name
+// ResolveRepo retrieves a repository by name
func (c *MultiRepoCache) ResolveRepo(name string) (*RepoCache, error) {
r, ok := c.repos[name]
if !ok {
@@ -69,12 +69,44 @@ func (c *MultiRepoCache) ResolveRepo(name string) (*RepoCache, error) {
return r, nil
}
-func (c *MultiRepoCache) RegisterObserver(observer Observer) {
+// 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
+// - if typename is empty, the observer is registered on all available entities
+// - if typename is not empty, the observer is registered on the matching entity type only
+func (c *MultiRepoCache) RegisterObserver(observer Observer, nameFilter string, typename string) error {
+ if nameFilter == "" {
+ for repoName, repo := range c.repos {
+ if typename == "" {
+ repo.registerAllObservers(repoName, observer)
+ } else {
+ if err := repo.registerObserver(repoName, typename, observer); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ }
+ r, err := c.ResolveRepo(nameFilter)
+ if err != nil {
+ return err
+ }
+ if typename == "" {
+ r.registerAllObservers(r.Name(), observer)
+ } else {
+ if err := r.registerObserver(r.Name(), typename, observer); err != nil {
+ return err
+ }
+ }
+ return nil
}
+// UnregisterObserver deregisters the observer from all repos and all entity types.
func (c *MultiRepoCache) UnregisterObserver(observer Observer) {
-
+ for _, repo := range c.repos {
+ repo.unregisterAllObservers(observer)
+ }
}
// Close will do anything that is needed to close the cache properly
@@ -5,6 +5,7 @@ import (
"io"
"os"
"strconv"
+ "strings"
"sync"
"github.com/git-bug/git-bug/entities/bug"
@@ -37,6 +38,8 @@ type cacheMgmt interface {
RemoveAll() error
MergeAll(remote string) <-chan entity.MergeResult
GetNamespace() string
+ RegisterObserver(repoName string, observer Observer)
+ UnregisterObserver(observer Observer)
Close() error
}
@@ -139,28 +142,6 @@ func NewRepoCacheNoEvents(r repository.ClockedRepo) (*RepoCache, error) {
return cache, nil
}
-func (c *RepoCache) registerObserver(repoRef string, typename string, observer Observer) {
- switch typename {
- case bug.Typename:
- c.bugs.RegisterObserver(repoRef, observer)
- case identity.Typename:
- c.identities.RegisterObserver(repoRef, observer)
- default:
- panic(fmt.Sprintf("unknown typename %q", typename))
- }
-}
-
-func (c *RepoCache) unregisterObserver(typename string, observer Observer) {
- switch typename {
- case bug.Typename:
- c.bugs.UnregisterObserver(observer)
- case identity.Typename:
- c.identities.UnregisterObserver(observer)
- default:
- panic(fmt.Sprintf("unknown typename %q", typename))
- }
-}
-
// Bugs gives access to the Bug entities
func (c *RepoCache) Bugs() *RepoCacheBug {
return c.bugs
@@ -251,6 +232,34 @@ func (c *RepoCache) buildCache(events chan BuildEvent) {
wg.Wait()
}
+func (c *RepoCache) registerObserver(repoName string, typename string, observer Observer) error {
+ switch typename {
+ case bug.Typename:
+ c.bugs.RegisterObserver(repoName, observer)
+ case identity.Typename:
+ c.identities.RegisterObserver(repoName, observer)
+ default:
+ var allTypenames []string
+ for _, subcache := range c.subcaches {
+ allTypenames = append(allTypenames, subcache.Typename())
+ }
+ return fmt.Errorf("unknown typename `%s`, available types are [%s]", typename, strings.Join(allTypenames, ", "))
+ }
+ return nil
+}
+
+func (c *RepoCache) registerAllObservers(repoName string, observer Observer) {
+ for _, subcache := range c.subcaches {
+ subcache.RegisterObserver(repoName, observer)
+ }
+}
+
+func (c *RepoCache) unregisterAllObservers(observer Observer) {
+ for _, subcache := range c.subcaches {
+ subcache.UnregisterObserver(observer)
+ }
+}
+
// repoIsAvailable check is the given repository is locked by a Cache.
// Note: this is a smart function that will clean the lock file if the
// corresponding process is not there anymore.
@@ -1,7 +1,6 @@
package cache
import (
- "fmt"
"strings"
"testing"
"time"
@@ -22,22 +21,21 @@ type observerEvent struct {
id entity.Id
}
+var _ Observer = &observer{}
+
type observer struct {
created []observerEvent
updated []observerEvent
removed []observerEvent
}
-func (o *observer) EntityEvent(event EntityEventType, typename string, id entity.Id) {
+func (o *observer) EntityEvent(event EntityEventType, _ string, typename string, id entity.Id) {
switch event {
case EntityEventCreated:
- fmt.Printf("Created %s: %s\n", typename, id.Human())
o.created = append(o.created, observerEvent{typename, id})
case EntityEventUpdated:
- fmt.Printf("Updated %s: %s\n", typename, id.Human())
o.updated = append(o.updated, observerEvent{typename, id})
case EntityEventRemoved:
- fmt.Printf("Removed %s: %s\n", typename, id.Human())
o.removed = append(o.removed, observerEvent{typename, id})
}
}
@@ -69,8 +67,8 @@ func TestCache(t *testing.T) {
require.NoError(t, err)
var obsIdentity, obsBug observer
- cache.RegisterObserver(identity.Typename, &obsIdentity)
- cache.RegisterObserver(bug.Typename, &obsBug)
+ require.NoError(t, cache.registerObserver("repotest", identity.Typename, &obsIdentity))
+ require.NoError(t, cache.registerObserver("repotest", bug.Typename, &obsBug))
// Create, set and get user identity
iden1, err := cache.Identities().New("RenĂŠ Descartes", "rene@descartes.fr")
@@ -149,6 +147,12 @@ func TestCache(t *testing.T) {
require.NoError(t, err)
require.Len(t, res, 1)
+ // Updating
+ _, _, err = bug1.AddComment("new comment")
+ require.NoError(t, err)
+ assertOberserverEvent(obsIdentity, 2, 0, 0)
+ assertOberserverEvent(obsBug, 2, 1, 0)
+
// Close
require.NoError(t, cache.Close())
require.Empty(t, cache.bugs.cached)
@@ -160,8 +164,8 @@ func TestCache(t *testing.T) {
// to check the signatures, we also load the identity used above
cache, err = NewRepoCacheNoEvents(repo)
require.NoError(t, err)
- cache.RegisterObserver(identity.Typename, &obsIdentity)
- cache.RegisterObserver(bug.Typename, &obsBug)
+ require.NoError(t, cache.registerObserver("repotest", identity.Typename, &obsIdentity))
+ require.NoError(t, cache.registerObserver("repotest", bug.Typename, &obsBug))
require.Len(t, cache.bugs.cached, 0)
require.Len(t, cache.bugs.excerpts, 2)
@@ -196,11 +200,11 @@ func TestCache(t *testing.T) {
err = cache.Identities().Remove(iden1.Id().String()[:10])
require.NoError(t, err)
assertOberserverEvent(obsIdentity, 2, 0, 1)
- assertOberserverEvent(obsBug, 2, 0, 0)
+ assertOberserverEvent(obsBug, 2, 1, 0)
err = cache.Bugs().Remove(bug1.Id().String()[:10])
require.NoError(t, err)
assertOberserverEvent(obsIdentity, 2, 0, 1)
- assertOberserverEvent(obsBug, 2, 0, 1)
+ assertOberserverEvent(obsBug, 2, 1, 1)
require.Len(t, cache.bugs.cached, 0)
require.Len(t, cache.bugs.excerpts, 1)
require.Len(t, cache.identities.cached, 0)
@@ -211,16 +215,16 @@ func TestCache(t *testing.T) {
_, err = cache.Identities().New("RenĂŠ Descartes", "rene@descartes.fr")
require.NoError(t, err)
assertOberserverEvent(obsIdentity, 3, 0, 1)
- assertOberserverEvent(obsBug, 2, 0, 1)
+ assertOberserverEvent(obsBug, 2, 1, 1)
_, _, err = cache.Bugs().NewRaw(iden2, time.Now().Unix(), "title", "message", nil, nil)
require.NoError(t, err)
assertOberserverEvent(obsIdentity, 3, 0, 1)
- assertOberserverEvent(obsBug, 3, 0, 1)
+ assertOberserverEvent(obsBug, 3, 1, 1)
err = cache.RemoveAll()
require.NoError(t, err)
assertOberserverEvent(obsIdentity, 3, 0, 3)
- assertOberserverEvent(obsBug, 3, 0, 3)
+ assertOberserverEvent(obsBug, 3, 1, 3)
require.Len(t, cache.bugs.cached, 0)
require.Len(t, cache.bugs.excerpts, 0)
require.Len(t, cache.identities.cached, 0)
@@ -61,7 +61,7 @@ type SubCache[EntityT entity.Interface, ExcerptT Excerpt, CacheT CacheEntity] st
lru lruIdCache
muObservers sync.RWMutex
- observers map[Observer]struct{}
+ observers map[Observer]string // observer --> repo name
}
func NewSubCache[EntityT entity.Interface, ExcerptT Excerpt, CacheT CacheEntity](
@@ -335,13 +335,13 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) Close() error {
return nil
}
-func (sc *SubCache[EntityT, ExcerptT, CacheT]) RegisterObserver(observer Observer) {
+func (sc *SubCache[EntityT, ExcerptT, CacheT]) RegisterObserver(repoName string, observer Observer) {
sc.muObservers.Lock()
defer sc.muObservers.Unlock()
if sc.observers == nil {
- sc.observers = make(map[Observer]struct{})
+ sc.observers = make(map[Observer]string)
}
- sc.observers[observer] = struct{}{}
+ sc.observers[observer] = repoName
}
func (sc *SubCache[EntityT, ExcerptT, CacheT]) UnregisterObserver(observer Observer) {
@@ -641,8 +641,8 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) entityUpdated(id entity.Id) error
// notifyObservers notifies all the observers when something happening for an entity
func (sc *SubCache[EntityT, ExcerptT, CacheT]) notifyObservers(event EntityEventType, id entity.Id) {
sc.muObservers.RLock()
- for observer := range sc.observers {
- observer.EntityEvent(event, sc.repo.sc.typename, id)
+ for observer, repoName := range sc.observers {
+ observer.EntityEvent(event, repoName, sc.typename, id)
}
sc.muObservers.RUnlock()
}