A website inspired by Last.fm that will keep track of your listening statistics
lastfm music statistics
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Based on the previously created table created repo interfaces and repo implementations for the database to interact with the newly created tables.

oscar345 809b1f4c 727b9bbc

+198
+8
internal/models/user.go
··· 1 + package models 2 + 3 + type User struct { 4 + ID int 5 + Name string 6 + Password string 7 + Email string 8 + }
+81
internal/repo/db/artist_follow.go
··· 1 + package db 2 + 3 + import ( 4 + "context" 5 + "database/sql" 6 + 7 + "github.com/oscar345/keeptrack/internal/models" 8 + "github.com/oscar345/keeptrack/internal/repo" 9 + "github.com/oscar345/keeptrack/pkg/database" 10 + "github.com/oscar345/keeptrack/pkg/pagination" 11 + ) 12 + 13 + type ArtistFollowRepoDB struct { 14 + db *sql.DB 15 + } 16 + 17 + var _ repo.ArtistFollowRepo = (*ArtistFollowRepoDB)(nil) 18 + 19 + func NewArtistFollowRepoDB(db *sql.DB) *ArtistFollowRepoDB { 20 + return &ArtistFollowRepoDB{db: db} 21 + } 22 + 23 + func (repo *ArtistFollowRepoDB) Follow(ctx context.Context, userID string, mbid string) error { 24 + values := map[string]any{"user_id": userID, "mbid": mbid} 25 + 26 + if _, err := database.Insert(ctx, repo.db, "artist_followers", values, database.WithIgnore()); err != nil { 27 + return err 28 + } 29 + 30 + return nil 31 + } 32 + 33 + func (repo *ArtistFollowRepoDB) Unfollow(ctx context.Context, userID string, mbid string) error { 34 + const statement = /*sql*/ ` 35 + DELETE FROM artist_followers 36 + WHERE user_id = ? AND mbid = ? 37 + ` 38 + 39 + if _, err := repo.db.ExecContext(ctx, statement, userID, mbid); err != nil { 40 + return err 41 + } 42 + 43 + return nil 44 + } 45 + 46 + func (repo *ArtistFollowRepoDB) List(ctx context.Context, userID string, filter pagination.Filter) ([]models.Artist, pagination.Page, error) { 47 + const statement = /*sql*/ ` 48 + WITH following AS ( 49 + SELECT 50 + artist.mbid, artist.name 51 + FROM artist 52 + INNER JOIN artist_followers ON artist.mbid = artist_followers.mbid 53 + WHERE artist_followers.user_id = ? 54 + 55 + ) 56 + SELECT 57 + following.mbid, 58 + following.name, 59 + COUNT(*) OVER () AS total 60 + FROM following 61 + ORDER BY following.name DESC 62 + LIMIT ? OFFSET ?; 63 + ` 64 + args := []any{userID, filter.Limit, filter.Offset} 65 + 66 + var total int 67 + artists, err := database.QueryMany(ctx, repo.db, statement, args, func(r *sql.Rows) (models.Artist, error) { 68 + var artist models.Artist 69 + if err := r.Scan(&artist.MBID, &artist.Name, &total); err != nil { 70 + return models.Artist{}, err 71 + 72 + } 73 + return artist, nil 74 + }) 75 + 76 + if err != nil { 77 + return nil, pagination.Page{}, err 78 + } 79 + 80 + return artists, pagination.New(filter, total), nil 81 + }
+85
internal/repo/db/user_follow.go
··· 1 + package db 2 + 3 + import ( 4 + "context" 5 + "database/sql" 6 + "fmt" 7 + 8 + "github.com/oscar345/keeptrack/internal/models" 9 + "github.com/oscar345/keeptrack/internal/repo" 10 + "github.com/oscar345/keeptrack/pkg/database" 11 + "github.com/oscar345/keeptrack/pkg/pagination" 12 + ) 13 + 14 + type UserFollowRepoDB struct { 15 + db *sql.DB 16 + } 17 + 18 + var _ repo.UserFollowRepo = (*UserFollowRepoDB)(nil) 19 + 20 + func NewUserFollowRepoDB(db *sql.DB) *UserFollowRepoDB { 21 + return &UserFollowRepoDB{db: db} 22 + } 23 + 24 + func (repo *UserFollowRepoDB) Follow(ctx context.Context, userID string, targetID string) error { 25 + values := map[string]any{"user_id": userID, "target_id": targetID} 26 + if _, err := database.Insert(ctx, repo.db, "user_follows", values, database.WithIgnore()); err != nil { 27 + return err 28 + } 29 + return nil 30 + } 31 + 32 + func (repo *UserFollowRepoDB) Unfollow(ctx context.Context, userID string, targetID string) error { 33 + const statement = /*sql*/ ` 34 + DELETE FROM user_follows 35 + WHERE user_id = ? AND target_id = ? 36 + ` 37 + 38 + if _, err := repo.db.ExecContext(ctx, statement, userID, targetID); err != nil { 39 + return err 40 + } 41 + 42 + return nil 43 + } 44 + 45 + func (repo *UserFollowRepoDB) list(ctx context.Context, userID string, filter pagination.Filter, whereColumn string) ([]models.User, pagination.Page, error) { 46 + var statement = /*sql*/ ` 47 + WITH followers AS ( 48 + SELECT 49 + users.id, users.name 50 + FROM users 51 + INNER JOIN user_follows ON users.id = user_follows.user_id 52 + WHERE %s = ? 53 + ) 54 + SELECT 55 + followers.id, followers.name, COUNT(*) OVER () AS total 56 + FROM followers 57 + ORDER BY followers.name 58 + LIMIT ? OFFSET ? 59 + ` 60 + 61 + args := []any{userID, filter.Limit(), filter.Offset()} 62 + statement = fmt.Sprintf(statement, whereColumn) 63 + 64 + var total int 65 + users, err := database.QueryMany(ctx, repo.db, statement, args, func(r *sql.Rows) (models.User, error) { 66 + var user models.User 67 + if err := r.Scan(&user.ID, &user.Name, &total); err != nil { 68 + return models.User{}, err 69 + } 70 + return user, nil 71 + }) 72 + if err != nil { 73 + return nil, pagination.Page{}, err 74 + } 75 + 76 + return users, pagination.New(filter, total), nil 77 + } 78 + 79 + func (repo *UserFollowRepoDB) ListFollowers(ctx context.Context, userID string, filter pagination.Filter) ([]models.User, pagination.Page, error) { 80 + return repo.list(ctx, userID, filter, "user_follows.target_id") 81 + } 82 + 83 + func (repo *UserFollowRepoDB) ListFollowing(ctx context.Context, userID string, filter pagination.Filter) ([]models.User, pagination.Page, error) { 84 + return repo.list(ctx, userID, filter, "user_follows.user_id") 85 + }
+24
internal/repo/repo.go
··· 18 18 GetCount(ctx context.Context, mbid string, filter filters.Count) (int, error) 19 19 } 20 20 21 + type ArtistFollowRepo interface { 22 + Follow(ctx context.Context, userID string, mbid string) error 23 + Unfollow(ctx context.Context, userID string, mbid string) error 24 + List(ctx context.Context, userID string, filter pagination.Filter) ([]models.Artist, pagination.Page, error) 25 + } 26 + 21 27 type RecordingRepo interface { 22 28 GetByID(ctx context.Context, mbid string) (models.Recording, error) 23 29 } 30 + 31 + type RecordingLikeRepo interface { 32 + Like(ctx context.Context, userID string, mbid string) error 33 + Unlike(ctx context.Context, userID string, mbid string) error 34 + List(ctx context.Context, userID string, filter pagination.Filter) ([]models.Recording, pagination.Page, error) 35 + } 36 + 37 + type UserRepo interface { 38 + Create(ctx context.Context, user models.User) error 39 + GetByID(ctx context.Context, id string) (models.User, error) 40 + } 41 + 42 + type UserFollowRepo interface { 43 + Follow(ctx context.Context, userID string, targetID string) error 44 + Unfollow(ctx context.Context, userID string, targetID string) error 45 + ListFollowers(ctx context.Context, userID string, filter pagination.Filter) ([]models.User, pagination.Page, error) 46 + ListFollowing(ctx context.Context, userID string, filter pagination.Filter) ([]models.User, pagination.Page, error) 47 + }