From 96546f6556c55aab61b6913f943144b2fa5a56de Mon Sep 17 00:00:00 2001 From: viktorstrate Date: Sun, 27 Dec 2020 20:07:54 +0100 Subject: [PATCH] Add resolvers for user albums and root_albums --- api/gqlgen.yml | 3 + api/graphql/generated.go | 168 ++++++++++++++++-------- api/graphql/resolvers/user.go | 33 +++++ api/graphql/schema.graphql | 4 +- ui/src/Pages/SettingsPage/UsersTable.js | 4 + 5 files changed, 155 insertions(+), 57 deletions(-) diff --git a/api/gqlgen.yml b/api/gqlgen.yml index 0f0c5e83..89eff49b 100644 --- a/api/gqlgen.yml +++ b/api/gqlgen.yml @@ -24,6 +24,9 @@ models: model: github.com/99designs/gqlgen/graphql.IntID User: model: github.com/photoview/photoview/api/graphql/models.User + fields: + albums: + resolver: true Media: model: github.com/photoview/photoview/api/graphql/models.Media MediaURL: diff --git a/api/graphql/generated.go b/api/graphql/generated.go index 65985d37..32128744 100644 --- a/api/graphql/generated.go +++ b/api/graphql/generated.go @@ -44,6 +44,7 @@ type ResolverRoot interface { Query() QueryResolver ShareToken() ShareTokenResolver Subscription() SubscriptionResolver + User() UserResolver } type DirectiveRoot struct { @@ -191,10 +192,11 @@ type ComplexityRoot struct { } User struct { - Admin func(childComplexity int) int - Albums func(childComplexity int) int - ID func(childComplexity int) int - Username func(childComplexity int) int + Admin func(childComplexity int) int + Albums func(childComplexity int) int + ID func(childComplexity int) int + RootAlbums func(childComplexity int) int + Username func(childComplexity int) int } VideoMetadata struct { @@ -268,6 +270,10 @@ type ShareTokenResolver interface { type SubscriptionResolver interface { Notification(ctx context.Context) (<-chan *models.Notification, error) } +type UserResolver interface { + Albums(ctx context.Context, obj *models.User) ([]*models.Album, error) + RootAlbums(ctx context.Context, obj *models.User) ([]*models.Album, error) +} type executableSchema struct { resolvers ResolverRoot @@ -1104,6 +1110,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.ID(childComplexity), true + case "User.rootAlbums": + if e.complexity.User.RootAlbums == nil { + break + } + + return e.complexity.User.RootAlbums(childComplexity), true + case "User.username": if e.complexity.User.Username == nil { break @@ -1436,8 +1449,10 @@ type User { username: String! #albums: [Album] # rootPath: String! @isAdmin - "Top level albums owned by this user" + "All albums owned by this user" albums: [Album!]! @isAdmin + "Top level albums owned by this user" + rootAlbums: [Album!]! @isAdmin admin: Boolean! #shareTokens: [ShareToken] } @@ -5931,15 +5946,15 @@ func (ec *executionContext) _User_albums(ctx context.Context, field graphql.Coll Object: "User", 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.Albums, nil + return ec.resolvers.User().Albums(rctx, obj) } directive1 := func(ctx context.Context) (interface{}, error) { if ec.directives.IsAdmin == nil { @@ -5955,10 +5970,10 @@ func (ec *executionContext) _User_albums(ctx context.Context, field graphql.Coll if tmp == nil { return nil, nil } - if data, ok := tmp.([]models.Album); ok { + if data, ok := tmp.([]*models.Album); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be []github.com/photoview/photoview/api/graphql/models.Album`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be []*github.com/photoview/photoview/api/graphql/models.Album`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -5970,9 +5985,64 @@ func (ec *executionContext) _User_albums(ctx context.Context, field graphql.Coll } return graphql.Null } - res := resTmp.([]models.Album) + res := resTmp.([]*models.Album) fc.Result = res - return ec.marshalNAlbum2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res) + return ec.marshalNAlbum2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) _User_rootAlbums(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "User", + Field: field, + Args: nil, + 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 ec.resolvers.User().RootAlbums(rctx, obj) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsAdmin == nil { + return nil, errors.New("directive isAdmin is not implemented") + } + return ec.directives.IsAdmin(ctx, obj, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.([]*models.Album); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be []*github.com/photoview/photoview/api/graphql/models.Album`, tmp) + }) + 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.Album) + fc.Result = res + return ec.marshalNAlbum2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res) } func (ec *executionContext) _User_admin(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { @@ -8425,22 +8495,45 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj case "id": out.Values[i] = ec._User_id(ctx, field, obj) if out.Values[i] == graphql.Null { - invalids++ + atomic.AddUint32(&invalids, 1) } case "username": out.Values[i] = ec._User_username(ctx, field, obj) if out.Values[i] == graphql.Null { - invalids++ + atomic.AddUint32(&invalids, 1) } case "albums": - out.Values[i] = ec._User_albums(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._User_albums(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "rootAlbums": + 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._User_rootAlbums(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "admin": out.Values[i] = ec._User_admin(ctx, field, obj) if out.Values[i] == graphql.Null { - invalids++ + atomic.AddUint32(&invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -8759,43 +8852,6 @@ func (ec *executionContext) marshalNAlbum2githubᚗcomᚋphotoviewᚋphotoview return ec._Album(ctx, sel, &v) } -func (ec *executionContext) marshalNAlbum2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx context.Context, sel ast.SelectionSet, v []models.Album) 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.marshalNAlbum2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - return ret -} - func (ec *executionContext) marshalNAlbum2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Album) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/api/graphql/resolvers/user.go b/api/graphql/resolvers/user.go index 50dc24bc..65029888 100644 --- a/api/graphql/resolvers/user.go +++ b/api/graphql/resolvers/user.go @@ -3,6 +3,7 @@ package resolvers import ( "context" + api "github.com/photoview/photoview/api/graphql" "github.com/photoview/photoview/api/graphql/auth" "github.com/photoview/photoview/api/graphql/models" "github.com/photoview/photoview/api/scanner" @@ -11,6 +12,14 @@ import ( "gorm.io/gorm" ) +type userResolver struct { + *Resolver +} + +func (r *Resolver) User() api.UserResolver { + return &userResolver{r} +} + func (r *queryResolver) User(ctx context.Context, filter *models.Filter) ([]*models.User, error) { var users []*models.User @@ -22,6 +31,30 @@ func (r *queryResolver) User(ctx context.Context, filter *models.Filter) ([]*mod return users, nil } +func (r *userResolver) Albums(ctx context.Context, user *models.User) ([]*models.Album, error) { + user.FillAlbums(r.Database) + + pointerAlbums := make([]*models.Album, len(user.Albums)) + for i, album := range user.Albums { + pointerAlbums[i] = &album + } + + return pointerAlbums, nil +} + +func (r *userResolver) RootAlbums(ctx context.Context, user *models.User) (albums []*models.Album, err error) { + + err = r.Database.Model(&user). + Where("albums.parent_album_id NOT IN (?)", + r.Database.Table("user_albums"). + Select("albums.id"). + Joins("JOIN albums ON albums.id = user_albums.album_id AND user_albums.user_id = ?", user.ID), + ).Or("albums.parent_album_id IS NULL"). + Association("Albums").Find(&albums) + + return +} + func (r *queryResolver) MyUser(ctx context.Context) (*models.User, error) { user := auth.UserFromContext(ctx) diff --git a/api/graphql/schema.graphql b/api/graphql/schema.graphql index 38bd197c..f4dcf526 100644 --- a/api/graphql/schema.graphql +++ b/api/graphql/schema.graphql @@ -172,8 +172,10 @@ type User { username: String! #albums: [Album] # rootPath: String! @isAdmin - "Top level albums owned by this user" + "All albums owned by this user" albums: [Album!]! @isAdmin + "Top level albums owned by this user" + rootAlbums: [Album!]! @isAdmin admin: Boolean! #shareTokens: [ShareToken] } diff --git a/ui/src/Pages/SettingsPage/UsersTable.js b/ui/src/Pages/SettingsPage/UsersTable.js index 1dc187e4..9c86aa09 100644 --- a/ui/src/Pages/SettingsPage/UsersTable.js +++ b/ui/src/Pages/SettingsPage/UsersTable.js @@ -13,6 +13,10 @@ const USERS_QUERY = gql` username # rootPath admin + albums { + id + filePath + } } } `