From c2a5b5d2f3f122aef25aee544a424fbd083e37aa Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Wed, 5 Feb 2020 16:14:21 +0100
Subject: [PATCH] Add album resolver

---
 api/gqlgen.yml                  |  2 +
 api/graphql/generated.go        | 87 ++++++++++++++++++++++++---------
 api/graphql/models/album.go     | 28 +++++++++++
 api/graphql/models/generated.go | 10 ----
 api/graphql/models/photo.go     | 16 +++++-
 api/graphql/resolvers/album.go  | 60 +++++++++++++++++++++++
 api/graphql/resolvers/photo.go  |  4 +-
 api/graphql/resolvers/root.go   |  9 ----
 8 files changed, 172 insertions(+), 44 deletions(-)
 create mode 100644 api/graphql/models/album.go
 create mode 100644 api/graphql/resolvers/album.go

diff --git a/api/gqlgen.yml b/api/gqlgen.yml
index dfbf32d..9575ccb 100644
--- a/api/gqlgen.yml
+++ b/api/gqlgen.yml
@@ -26,3 +26,5 @@ models:
     model: github.com/viktorstrate/photoview/api/graphql/models.Photo
   PhotoURL:
     model: github.com/viktorstrate/photoview/api/graphql/models.PhotoURL
+  Album:
+    model: github.com/viktorstrate/photoview/api/graphql/models.Album
diff --git a/api/graphql/generated.go b/api/graphql/generated.go
index 10d274b..f29a133 100644
--- a/api/graphql/generated.go
+++ b/api/graphql/generated.go
@@ -37,6 +37,7 @@ type Config struct {
 }
 
 type ResolverRoot interface {
+	Album() AlbumResolver
 	Mutation() MutationResolver
 	Photo() PhotoResolver
 	Query() QueryResolver
@@ -124,6 +125,12 @@ type ComplexityRoot struct {
 	}
 }
 
+type AlbumResolver interface {
+	Photos(ctx context.Context, obj *models.Album) ([]*models.Photo, error)
+	SubAlbums(ctx context.Context, obj *models.Album) ([]*models.Album, error)
+	ParentAlbum(ctx context.Context, obj *models.Album) (*models.Album, error)
+	Owner(ctx context.Context, obj *models.Album) (*models.User, error)
+}
 type MutationResolver interface {
 	AuthorizeUser(ctx context.Context, username string, password string) (*models.AuthorizeResult, error)
 	RegisterUser(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error)
@@ -874,13 +881,13 @@ func (ec *executionContext) _Album_id(ctx context.Context, field graphql.Collect
 		Object:   "Album",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 	ctx = graphql.WithResolverContext(ctx, rctx)
 	ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.ID, nil
+		return obj.ID(), nil
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -926,10 +933,10 @@ func (ec *executionContext) _Album_title(ctx context.Context, field graphql.Coll
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(*string)
+	res := resTmp.(string)
 	rctx.Result = res
 	ctx = ec.Tracer.StartFieldChildExecution(ctx)
-	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
+	return ec.marshalOString2string(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Album_photos(ctx context.Context, field graphql.CollectedField, obj *models.Album) (ret graphql.Marshaler) {
@@ -945,13 +952,13 @@ func (ec *executionContext) _Album_photos(ctx context.Context, field graphql.Col
 		Object:   "Album",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 	ctx = graphql.WithResolverContext(ctx, rctx)
 	ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Photos, nil
+		return ec.resolvers.Album().Photos(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -979,13 +986,13 @@ func (ec *executionContext) _Album_subAlbums(ctx context.Context, field graphql.
 		Object:   "Album",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 	ctx = graphql.WithResolverContext(ctx, rctx)
 	ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.SubAlbums, nil
+		return ec.resolvers.Album().SubAlbums(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -1013,13 +1020,13 @@ func (ec *executionContext) _Album_parentAlbum(ctx context.Context, field graphq
 		Object:   "Album",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 	ctx = graphql.WithResolverContext(ctx, rctx)
 	ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.ParentAlbum, nil
+		return ec.resolvers.Album().ParentAlbum(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -1047,13 +1054,13 @@ func (ec *executionContext) _Album_owner(ctx context.Context, field graphql.Coll
 		Object:   "Album",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 	ctx = graphql.WithResolverContext(ctx, rctx)
 	ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Owner, nil
+		return ec.resolvers.Album().Owner(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -1099,10 +1106,10 @@ func (ec *executionContext) _Album_path(ctx context.Context, field graphql.Colle
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(*string)
+	res := resTmp.(string)
 	rctx.Result = res
 	ctx = ec.Tracer.StartFieldChildExecution(ctx)
-	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
+	return ec.marshalOString2string(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _AuthorizeResult_success(ctx context.Context, field graphql.CollectedField, obj *models.AuthorizeResult) (ret graphql.Marshaler) {
@@ -3901,21 +3908,57 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob
 		case "id":
 			out.Values[i] = ec._Album_id(ctx, field, obj)
 			if out.Values[i] == graphql.Null {
-				invalids++
+				atomic.AddUint32(&invalids, 1)
 			}
 		case "title":
 			out.Values[i] = ec._Album_title(ctx, field, obj)
 		case "photos":
-			out.Values[i] = ec._Album_photos(ctx, field, obj)
+			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._Album_photos(ctx, field, obj)
+				return res
+			})
 		case "subAlbums":
-			out.Values[i] = ec._Album_subAlbums(ctx, field, obj)
+			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._Album_subAlbums(ctx, field, obj)
+				return res
+			})
 		case "parentAlbum":
-			out.Values[i] = ec._Album_parentAlbum(ctx, field, obj)
+			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._Album_parentAlbum(ctx, field, obj)
+				return res
+			})
 		case "owner":
-			out.Values[i] = ec._Album_owner(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._Album_owner(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "path":
 			out.Values[i] = ec._Album_path(ctx, field, obj)
 		default:
diff --git a/api/graphql/models/album.go b/api/graphql/models/album.go
new file mode 100644
index 0000000..293625b
--- /dev/null
+++ b/api/graphql/models/album.go
@@ -0,0 +1,28 @@
+package models
+
+import (
+	"database/sql"
+	"strconv"
+)
+
+type Album struct {
+	AlbumID     int
+	Title       string
+	ParentAlbum *int
+	OwnerID     int
+	Path        string
+}
+
+func (a *Album) ID() string {
+	return strconv.Itoa(a.AlbumID)
+}
+
+func NewAlbumFromRow(row *sql.Row) (*Album, error) {
+	album := Album{}
+
+	if err := row.Scan(&album.AlbumID, &album.Title, &album.ParentAlbum, &album.OwnerID, &album.Path); err != nil {
+		return nil, err
+	}
+
+	return &album, nil
+}
diff --git a/api/graphql/models/generated.go b/api/graphql/models/generated.go
index 87f0b5a..925ad41 100644
--- a/api/graphql/models/generated.go
+++ b/api/graphql/models/generated.go
@@ -6,16 +6,6 @@ import (
 	"time"
 )
 
-type Album struct {
-	ID          string   `json:"id"`
-	Title       *string  `json:"title"`
-	Photos      []*Photo `json:"photos"`
-	SubAlbums   []*Album `json:"subAlbums"`
-	ParentAlbum *Album   `json:"parentAlbum"`
-	Owner       *User    `json:"owner"`
-	Path        *string  `json:"path"`
-}
-
 type AuthorizeResult struct {
 	Success bool    `json:"success"`
 	Status  string  `json:"status"`
diff --git a/api/graphql/models/photo.go b/api/graphql/models/photo.go
index 8a9c021..34a8f7d 100644
--- a/api/graphql/models/photo.go
+++ b/api/graphql/models/photo.go
@@ -16,7 +16,7 @@ type Photo struct {
 }
 
 type PhotoURL struct {
-	UrlId  int
+	UrlID  int
 	Token  string
 	Width  int
 	Height int
@@ -36,6 +36,20 @@ func NewPhotoFromRow(row *sql.Row) (*Photo, error) {
 	return &photo, nil
 }
 
+func NewPhotosFromRows(rows *sql.Rows) ([]*Photo, error) {
+	photos := make([]*Photo, 0)
+
+	for rows.Next() {
+		var photo Photo
+		if err := rows.Scan(&photo.PhotoID, &photo.Title, &photo.Path, &photo.OriginalUrl, &photo.ThumbnailUrl, &photo.AlbumId, &photo.ExifId); err != nil {
+			return nil, err
+		}
+		photos = append(photos, &photo)
+	}
+
+	return photos, nil
+}
+
 func (p *PhotoURL) URL() string {
 	return "URL:" + p.Token
 }
diff --git a/api/graphql/resolvers/album.go b/api/graphql/resolvers/album.go
new file mode 100644
index 0000000..a39ca97
--- /dev/null
+++ b/api/graphql/resolvers/album.go
@@ -0,0 +1,60 @@
+package resolvers
+
+import (
+	"context"
+
+	api "github.com/viktorstrate/photoview/api/graphql"
+	"github.com/viktorstrate/photoview/api/graphql/auth"
+	"github.com/viktorstrate/photoview/api/graphql/models"
+)
+
+func (r *queryResolver) MyAlbums(ctx context.Context) ([]*models.Album, error) {
+	panic("Not implemented")
+}
+func (r *queryResolver) Album(ctx context.Context, id *string) (*models.Album, error) {
+	user := auth.UserFromContext(ctx)
+	if user == nil {
+		return nil, auth.ErrUnauthorized
+	}
+
+	row := r.Database.QueryRow("SELECT * FROM album WHERE album_id = ? AND owner_id = ?", id, user.UserID)
+	album, err := models.NewAlbumFromRow(row)
+	if err != nil {
+		return nil, err
+	}
+
+	return album, nil
+}
+
+func (r *Resolver) Album() api.AlbumResolver {
+	return &albumResolver{r}
+}
+
+type albumResolver struct{ *Resolver }
+
+func (r *albumResolver) Photos(ctx context.Context, obj *models.Album) ([]*models.Photo, error) {
+	photoRows, err := r.Database.Query("SELECT photo.* FROM album, photo WHERE album.album_id = ? AND photo.album_id = album.album_id", obj.AlbumID)
+	if err != nil {
+		return nil, err
+	}
+	defer photoRows.Close()
+
+	photos, err := models.NewPhotosFromRows(photoRows)
+	if err != nil {
+		return nil, err
+	}
+
+	return photos, nil
+}
+
+func (r *albumResolver) SubAlbums(ctx context.Context, obj *models.Album) ([]*models.Album, error) {
+	panic("not implemented")
+}
+
+func (r *albumResolver) ParentAlbum(ctx context.Context, obj *models.Album) (*models.Album, error) {
+	panic("not implemented")
+}
+
+func (r *albumResolver) Owner(ctx context.Context, obj *models.Album) (*models.User, error) {
+	panic("not implemented")
+}
diff --git a/api/graphql/resolvers/photo.go b/api/graphql/resolvers/photo.go
index 20387ea..6a65740 100644
--- a/api/graphql/resolvers/photo.go
+++ b/api/graphql/resolvers/photo.go
@@ -44,7 +44,7 @@ func (r *photoResolver) Original(ctx context.Context, obj *models.Photo) (*model
 	row := r.Database.QueryRow("SELECT photo_url.* FROM photo, photo_url WHERE photo.photo_id = ? AND photo.original_url = photo_url.url_id", obj.PhotoID)
 
 	var photoUrl models.PhotoURL
-	if err := row.Scan(&photoUrl.UrlId, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil {
+	if err := row.Scan(&photoUrl.UrlID, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil {
 		return nil, err
 	}
 
@@ -55,7 +55,7 @@ func (r *photoResolver) Thumbnail(ctx context.Context, obj *models.Photo) (*mode
 	row := r.Database.QueryRow("SELECT photo_url.* FROM photo, photo_url WHERE photo.photo_id = ? AND photo.thumbnail_url = photo_url.url_id", obj.PhotoID)
 
 	var photoUrl models.PhotoURL
-	if err := row.Scan(&photoUrl.UrlId, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil {
+	if err := row.Scan(&photoUrl.UrlID, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil {
 		return nil, err
 	}
 
diff --git a/api/graphql/resolvers/root.go b/api/graphql/resolvers/root.go
index 5c0084d..5cab073 100644
--- a/api/graphql/resolvers/root.go
+++ b/api/graphql/resolvers/root.go
@@ -1,11 +1,9 @@
 package resolvers
 
 import (
-	"context"
 	"database/sql"
 
 	api "github.com/viktorstrate/photoview/api/graphql"
-	"github.com/viktorstrate/photoview/api/graphql/models"
 )
 
 //go:generate go run github.com/99designs/gqlgen
@@ -25,10 +23,3 @@ func (r *Resolver) Query() api.QueryResolver {
 type mutationResolver struct{ *Resolver }
 
 type queryResolver struct{ *Resolver }
-
-func (r *queryResolver) MyAlbums(ctx context.Context) ([]*models.Album, error) {
-	panic("Not implemented")
-}
-func (r *queryResolver) Album(ctx context.Context, id *string) (*models.Album, error) {
-	panic("Not implemented")
-}
