API: basic ACL riggings

This commit is contained in:
Drew DeVault 2021-02-16 14:17:52 -05:00
parent cccc0c0ea6
commit 56881ae3e4
5 changed files with 324 additions and 163 deletions

View File

@ -50,6 +50,7 @@ type ResolverRoot interface {
TicketMention() TicketMentionResolver
TicketSubscription() TicketSubscriptionResolver
Tracker() TrackerResolver
TrackerACL() TrackerACLResolver
TrackerSubscription() TrackerSubscriptionResolver
User() UserResolver
UserMention() UserMentionResolver
@ -345,11 +346,15 @@ type TrackerResolver interface {
Tickets(ctx context.Context, obj *model.Tracker, cursor *model1.Cursor) (*model.TicketCursor, error)
Labels(ctx context.Context, obj *model.Tracker, cursor *model1.Cursor) (*model.LabelCursor, error)
Acls(ctx context.Context, obj *model.Tracker, cursor *model1.Cursor) (*model.ACLCursor, error)
Acls(ctx context.Context, obj *model.Tracker, cursor *model1.Cursor) (*model.ACLCursor, error)
Subscription(ctx context.Context, obj *model.Tracker) (*model.TrackerSubscription, error)
ACL(ctx context.Context, obj *model.Tracker) (model.ACL, error)
}
type TrackerACLResolver interface {
Tracker(ctx context.Context, obj *model.TrackerACL) (*model.Tracker, error)
Entity(ctx context.Context, obj *model.TrackerACL) (model.Entity, error)
}
type TrackerSubscriptionResolver interface {
Tracker(ctx context.Context, obj *model.TrackerSubscription) (*model.Tracker, error)
}
@ -1521,9 +1526,11 @@ type Tracker {
tickets(cursor: Cursor): TicketCursor! @access(scope: TICKETS, kind: RO)
labels(cursor: Cursor): LabelCursor!
acls(cursor: Cursor): ACLCursor! @access(scope: ACLS, kind: RO)
defaultACLs: DefaultACLs @access(scope: ACLS, kind: RO)
# Only available to the tracker owner:
acls(cursor: Cursor): ACLCursor! @access(scope: ACLS, kind: RO)
# If the authenticated user is subscribed to this tracker, this is that
# subscription.
subscription: TrackerSubscription @access(scope: SUBSCRIPTIONS, kind: RO)
@ -1797,7 +1804,7 @@ type LabelCursor {
# back into the same endpoint to retrieve another page. If the cursor is null,
# there are no remaining results to return.
type ACLCursor {
results: [ACL]!
results: [TrackerACL]!
cursor: Cursor
}
@ -2189,9 +2196,9 @@ func (ec *executionContext) _ACLCursor_results(ctx context.Context, field graphq
}
return graphql.Null
}
res := resTmp.([]model.ACL)
res := resTmp.([]*model.TrackerACL)
fc.Result = res
return ec.marshalNACL2ᚕgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐACL(ctx, field.Selections, res)
return ec.marshalNTrackerACL2ᚕgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐTrackerACL(ctx, field.Selections, res)
}
func (ec *executionContext) _ACLCursor_cursor(ctx context.Context, field graphql.CollectedField, obj *model.ACLCursor) (ret graphql.Marshaler) {
@ -6219,6 +6226,66 @@ func (ec *executionContext) _Tracker_labels(ctx context.Context, field graphql.C
return ec.marshalNLabelCursor2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐLabelCursor(ctx, field.Selections, res)
}
func (ec *executionContext) _Tracker_defaultACLs(ctx context.Context, field graphql.CollectedField, obj *model.Tracker) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Tracker",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.DefaultACLs, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "ACLS")
if err != nil {
return nil, err
}
kind, err := ec.unmarshalNAccessKind2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RO")
if err != nil {
return nil, err
}
if ec.directives.Access == nil {
return nil, errors.New("directive access is not implemented")
}
return ec.directives.Access(ctx, obj, directive0, scope, kind)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.DefaultACLs); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *git.sr.ht/~sircmpwn/todo.sr.ht/api/graph/model.DefaultACLs`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*model.DefaultACLs)
fc.Result = res
return ec.marshalODefaultACLs2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐDefaultACLs(ctx, field.Selections, res)
}
func (ec *executionContext) _Tracker_acls(ctx context.Context, field graphql.CollectedField, obj *model.Tracker) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -6289,66 +6356,6 @@ func (ec *executionContext) _Tracker_acls(ctx context.Context, field graphql.Col
return ec.marshalNACLCursor2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐACLCursor(ctx, field.Selections, res)
}
func (ec *executionContext) _Tracker_defaultACLs(ctx context.Context, field graphql.CollectedField, obj *model.Tracker) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Tracker",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.DefaultACLs, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "ACLS")
if err != nil {
return nil, err
}
kind, err := ec.unmarshalNAccessKind2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RO")
if err != nil {
return nil, err
}
if ec.directives.Access == nil {
return nil, errors.New("directive access is not implemented")
}
return ec.directives.Access(ctx, obj, directive0, scope, kind)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.DefaultACLs); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *git.sr.ht/~sircmpwn/todo.sr.ht/api/graph/model.DefaultACLs`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*model.DefaultACLs)
fc.Result = res
return ec.marshalODefaultACLs2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐDefaultACLs(ctx, field.Selections, res)
}
func (ec *executionContext) _Tracker_subscription(ctx context.Context, field graphql.CollectedField, obj *model.Tracker) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -6522,14 +6529,14 @@ func (ec *executionContext) _TrackerACL_tracker(ctx context.Context, field graph
Object: "TrackerACL",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Tracker, nil
return ec.resolvers.TrackerACL().Tracker(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@ -6557,15 +6564,15 @@ func (ec *executionContext) _TrackerACL_entity(ctx context.Context, field graphq
Object: "TrackerACL",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Entity, nil
return ec.resolvers.TrackerACL().Entity(rctx, obj)
}
directive1 := func(ctx context.Context) (interface{}, error) {
scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "PROFILE")
@ -10132,6 +10139,8 @@ func (ec *executionContext) _Tracker(ctx context.Context, sel ast.SelectionSet,
}
return res
})
case "defaultACLs":
out.Values[i] = ec._Tracker_defaultACLs(ctx, field, obj)
case "acls":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
@ -10146,8 +10155,6 @@ func (ec *executionContext) _Tracker(ctx context.Context, sel ast.SelectionSet,
}
return res
})
case "defaultACLs":
out.Values[i] = ec._Tracker_defaultACLs(ctx, field, obj)
case "subscription":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
@ -10195,47 +10202,65 @@ func (ec *executionContext) _TrackerACL(ctx context.Context, sel ast.SelectionSe
case "id":
out.Values[i] = ec._TrackerACL_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "created":
out.Values[i] = ec._TrackerACL_created(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "tracker":
out.Values[i] = ec._TrackerACL_tracker(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TrackerACL_tracker(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "entity":
out.Values[i] = ec._TrackerACL_entity(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TrackerACL_entity(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "browse":
out.Values[i] = ec._TrackerACL_browse(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "submit":
out.Values[i] = ec._TrackerACL_submit(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "comment":
out.Values[i] = ec._TrackerACL_comment(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "edit":
out.Values[i] = ec._TrackerACL_edit(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "triage":
out.Values[i] = ec._TrackerACL_triage(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
default:
panic("unknown field " + strconv.Quote(field.Name))
@ -10758,43 +10783,6 @@ func (ec *executionContext) marshalNACL2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsr
return ec._ACL(ctx, sel, v)
}
func (ec *executionContext) marshalNACL2ᚕgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐACL(ctx context.Context, sel ast.SelectionSet, v []model.ACL) 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.marshalOACL2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐACL(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalNACLCursor2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐACLCursor(ctx context.Context, sel ast.SelectionSet, v model.ACLCursor) graphql.Marshaler {
return ec._ACLCursor(ctx, sel, &v)
}
@ -11282,6 +11270,43 @@ func (ec *executionContext) marshalNTracker2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodo
return ec._Tracker(ctx, sel, v)
}
func (ec *executionContext) marshalNTrackerACL2ᚕᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐTrackerACL(ctx context.Context, sel ast.SelectionSet, v []*model.TrackerACL) 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.marshalOTrackerACL2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐTrackerACL(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalNTrackerCursor2gitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐTrackerCursor(ctx context.Context, sel ast.SelectionSet, v model.TrackerCursor) graphql.Marshaler {
return ec._TrackerCursor(ctx, sel, &v)
}
@ -11723,6 +11748,13 @@ func (ec *executionContext) marshalOTracker2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodo
return ec._Tracker(ctx, sel, v)
}
func (ec *executionContext) marshalOTrackerACL2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐTrackerACL(ctx context.Context, sel ast.SelectionSet, v *model.TrackerACL) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._TrackerACL(ctx, sel, v)
}
func (ec *executionContext) marshalOTrackerCursor2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋtodoᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐTrackerCursor(ctx context.Context, sel ast.SelectionSet, v *model.TrackerCursor) graphql.Marshaler {
if v == nil {
return graphql.Null

125
api/graph/model/acl.go Normal file
View File

@ -0,0 +1,125 @@
package model
import (
"context"
"database/sql"
"strconv"
"time"
sq "github.com/Masterminds/squirrel"
"git.sr.ht/~sircmpwn/core-go/database"
"git.sr.ht/~sircmpwn/core-go/model"
)
type ACL interface {
IsACL()
}
type DefaultACL struct {
Browse bool `json:"browse"`
Submit bool `json:"submit"`
Comment bool `json:"comment"`
Edit bool `json:"edit"`
Triage bool `json:"triage"`
}
func (DefaultACL) IsACL() {}
type DefaultACLs struct {
Anonymous ACL `json:"anonymous"`
Submitter ACL `json:"submitter"`
LoggedIn ACL `json:"logged_in"`
}
type TrackerACL struct {
ID int `json:"id"`
Created time.Time `json:"created"`
Browse bool `json:"browse"`
Submit bool `json:"submit"`
Comment bool `json:"comment"`
Edit bool `json:"edit"`
Triage bool `json:"triage"`
TrackerID int
UserID int
alias string
fields *database.ModelFields
}
func (TrackerACL) IsACL() {}
func (acl *TrackerACL) As(alias string) *TrackerACL {
acl.alias = alias
return acl
}
func (acl *TrackerACL) Alias() string {
return acl.alias
}
func (acl *TrackerACL) Table() string {
return `"user_access"`
}
func (acl *TrackerACL) Fields() *database.ModelFields {
if acl.fields != nil {
return acl.fields
}
acl.fields = &database.ModelFields{
Fields: []*database.FieldMap{
{ "id", "id", &acl.ID },
{ "created", "created", &acl.Created },
// Always fetch:
{ "id", "", &acl.ID },
{ "user_id", "", &acl.UserID },
{ "tracker_id", "", &acl.TrackerID },
},
}
return acl.fields
}
func (acl *TrackerACL) QueryWithCursor(ctx context.Context, runner sq.BaseRunner,
q sq.SelectBuilder, cur *model.Cursor) ([]*TrackerACL, *model.Cursor) {
var (
err error
rows *sql.Rows
)
if cur.Next != "" {
next, _ := strconv.ParseInt(cur.Next, 10, 64)
q = q.Where(database.WithAlias(acl.alias, "id")+"<= ?", next)
}
q = q.
OrderBy(database.WithAlias(acl.alias, "id")).
Limit(uint64(cur.Count + 1))
if rows, err = q.RunWith(runner).QueryContext(ctx); err != nil {
panic(err)
}
defer rows.Close()
var acls []*TrackerACL
for rows.Next() {
var acl TrackerACL
if err := rows.Scan(database.Scan(ctx, &acl)...); err != nil {
panic(err)
}
acls = append(acls, &acl)
}
if len(acls) > cur.Count {
cur = &model.Cursor{
Count: cur.Count,
Next: strconv.Itoa(acls[len(acls)-1].ID),
Search: cur.Search,
}
acls = acls[:cur.Count]
} else {
cur = nil
}
return acls, cur
}

View File

@ -11,10 +11,6 @@ import (
"git.sr.ht/~sircmpwn/core-go/model"
)
type ACL interface {
IsACL()
}
type Entity interface {
IsEntity()
}
@ -24,26 +20,10 @@ type EventDetail interface {
}
type ACLCursor struct {
Results []ACL `json:"results"`
Results []*TrackerACL `json:"results"`
Cursor *model.Cursor `json:"cursor"`
}
type DefaultACL struct {
Browse bool `json:"browse"`
Submit bool `json:"submit"`
Comment bool `json:"comment"`
Edit bool `json:"edit"`
Triage bool `json:"triage"`
}
func (DefaultACL) IsACL() {}
type DefaultACLs struct {
Anonymous ACL `json:"anonymous"`
Submitter ACL `json:"submitter"`
LoggedIn ACL `json:"logged_in"`
}
type EventCursor struct {
Results []*Event `json:"results"`
Cursor *model.Cursor `json:"cursor"`
@ -64,20 +44,6 @@ type TicketCursor struct {
Cursor *model.Cursor `json:"cursor"`
}
type TrackerACL struct {
ID int `json:"id"`
Created time.Time `json:"created"`
Tracker *Tracker `json:"tracker"`
Entity Entity `json:"entity"`
Browse bool `json:"browse"`
Submit bool `json:"submit"`
Comment bool `json:"comment"`
Edit bool `json:"edit"`
Triage bool `json:"triage"`
}
func (TrackerACL) IsACL() {}
type TrackerCursor struct {
Results []*Tracker `json:"results"`
Cursor *model.Cursor `json:"cursor"`

View File

@ -82,9 +82,11 @@ type Tracker {
tickets(cursor: Cursor): TicketCursor! @access(scope: TICKETS, kind: RO)
labels(cursor: Cursor): LabelCursor!
acls(cursor: Cursor): ACLCursor! @access(scope: ACLS, kind: RO)
defaultACLs: DefaultACLs @access(scope: ACLS, kind: RO)
# Only available to the tracker owner:
acls(cursor: Cursor): ACLCursor! @access(scope: ACLS, kind: RO)
# If the authenticated user is subscribed to this tracker, this is that
# subscription.
subscription: TrackerSubscription @access(scope: SUBSCRIPTIONS, kind: RO)
@ -358,7 +360,7 @@ type LabelCursor {
# back into the same endpoint to retrieve another page. If the cursor is null,
# there are no remaining results to return.
type ACLCursor {
results: [ACL]!
results: [TrackerACL]!
cursor: Cursor
}

View File

@ -422,7 +422,31 @@ func (r *trackerResolver) Labels(ctx context.Context, obj *model.Tracker, cursor
}
func (r *trackerResolver) Acls(ctx context.Context, obj *model.Tracker, cursor *coremodel.Cursor) (*model.ACLCursor, error) {
panic(fmt.Errorf("not implemented"))
if obj.OwnerID != auth.ForContext(ctx).UserID {
return nil, errors.New("Access denied")
}
if cursor == nil {
cursor = coremodel.NewCursor(nil)
}
var acls []*model.TrackerACL
if err := database.WithTx(ctx, &sql.TxOptions{
Isolation: 0,
ReadOnly: true,
}, func(tx *sql.Tx) error {
acl := (&model.TrackerACL{}).As(`ua`)
query := database.
Select(ctx, acl).
From(`user_access ua`).
Where(`ua.tracker_id = ?`, obj.ID)
acls, cursor = acl.QueryWithCursor(ctx, tx, query, cursor)
return nil
}); err != nil {
return nil, err
}
return &model.ACLCursor{acls, cursor}, nil
}
func (r *trackerResolver) Subscription(ctx context.Context, obj *model.Tracker) (*model.TrackerSubscription, error) {
@ -435,6 +459,14 @@ func (r *trackerResolver) ACL(ctx context.Context, obj *model.Tracker) (model.AC
panic(fmt.Errorf("not implemented"))
}
func (r *trackerACLResolver) Tracker(ctx context.Context, obj *model.TrackerACL) (*model.Tracker, error) {
panic(fmt.Errorf("not implemented"))
}
func (r *trackerACLResolver) Entity(ctx context.Context, obj *model.TrackerACL) (model.Entity, error) {
panic(fmt.Errorf("not implemented"))
}
func (r *trackerSubscriptionResolver) Tracker(ctx context.Context, obj *model.TrackerSubscription) (*model.Tracker, error) {
return loaders.ForContext(ctx).TrackersByID.Load(obj.TrackerID)
}
@ -525,6 +557,9 @@ func (r *Resolver) TicketSubscription() api.TicketSubscriptionResolver {
// Tracker returns api.TrackerResolver implementation.
func (r *Resolver) Tracker() api.TrackerResolver { return &trackerResolver{r} }
// TrackerACL returns api.TrackerACLResolver implementation.
func (r *Resolver) TrackerACL() api.TrackerACLResolver { return &trackerACLResolver{r} }
// TrackerSubscription returns api.TrackerSubscriptionResolver implementation.
func (r *Resolver) TrackerSubscription() api.TrackerSubscriptionResolver {
return &trackerSubscriptionResolver{r}
@ -548,6 +583,7 @@ type ticketResolver struct{ *Resolver }
type ticketMentionResolver struct{ *Resolver }
type ticketSubscriptionResolver struct{ *Resolver }
type trackerResolver struct{ *Resolver }
type trackerACLResolver struct{ *Resolver }
type trackerSubscriptionResolver struct{ *Resolver }
type userResolver struct{ *Resolver }
type userMentionResolver struct{ *Resolver }