diff --git a/api/dbv1/queries/get_trending_tracks.sql b/api/dbv1/queries/get_trending_tracks.sql deleted file mode 100644 index e69de29b..00000000 diff --git a/api/fixture_test.go b/api/fixture_test.go index e6551232..0162e45e 100644 --- a/api/fixture_test.go +++ b/api/fixture_test.go @@ -106,6 +106,16 @@ var ( "image_url": nil, } + trackTrendingScoreBaseRow = map[string]any{ + "track_id": nil, + "type": "TRACKS", + "genre": nil, + "version": "pnagD", + "time_range": nil, + "score": nil, + "created_at": time.Now(), + } + connectedWalletsBaseRow = map[string]any{ "id": nil, "user_id": nil, diff --git a/api/server.go b/api/server.go index 4fd42f8f..b9ffb773 100644 --- a/api/server.go +++ b/api/server.go @@ -147,6 +147,8 @@ func NewApiServer(config Config) *ApiServer { // Tracks g.Get("/tracks", app.v1Tracks) + g.Get("/tracks/trending", app.v1Trending) + g.Use("/tracks/:trackId", app.requireTrackIdMiddleware) g.Get("/tracks/:trackId", app.v1Track) g.Get("/tracks/:trackId/reposts", app.v1TracksReposts) diff --git a/api/server_test.go b/api/server_test.go index 433dc082..9983efb8 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -67,6 +67,7 @@ func TestMain(m *testing.M) { insertFixtures("follows", followBaseRow, "testdata/follow_fixtures.csv") insertFixtures("reposts", repostBaseRow, "testdata/repost_fixtures.csv") insertFixtures("developer_apps", developerAppBaseRow, "testdata/developer_app_fixtures.csv") + insertFixtures("track_trending_scores", trackTrendingScoreBaseRow, "testdata/track_trending_scores_fixtures.csv") insertFixtures("associated_wallets", connectedWalletsBaseRow, "testdata/connected_wallets_fixtures.csv") // index to es / os diff --git a/api/testdata/track_fixtures.csv b/api/testdata/track_fixtures.csv index 91e526b8..555a5eca 100644 --- a/api/testdata/track_fixtures.csv +++ b/api/testdata/track_fixtures.csv @@ -1,6 +1,7 @@ -track_id,owner_id,title,is_unlisted,stream_conditions,download_conditions -200,2,Culca Canyon,f,, -201,2,Turkey Time DEMO,t,, -300,3,Follow Gated Download,f,,"{""follow_user_id"": 3}" -301,3,Pay Gated Download,f,,"{""usdc_purchase"": {""price"": 135, ""splits"": [{""user_id"": 3, ""percentage"": 100.0}]}}" -302,3,Tip Gated Stream,f," {""tip_user_id"": 859175075}", +track_id,genre,owner_id,title,is_unlisted,stream_conditions,download_conditions +200,Electronic,2,Culca Canyon,f,, +201,Alternative,2,Turkey Time DEMO,t,, +202,Alternative,2,Turkey Time (live),f,, +300,Electronic,3,Follow Gated Download,f,,"{""follow_user_id"": 3}" +301,Electronic,3,Pay Gated Download,f,,"{""usdc_purchase"": {""price"": 135, ""splits"": [{""user_id"": 3, ""percentage"": 100.0}]}}" +302,Electronic,3,Tip Gated Stream,f," {""tip_user_id"": 859175075}", diff --git a/api/testdata/track_trending_scores_fixtures.csv b/api/testdata/track_trending_scores_fixtures.csv new file mode 100644 index 00000000..439d99b0 --- /dev/null +++ b/api/testdata/track_trending_scores_fixtures.csv @@ -0,0 +1,7 @@ +track_id,genre,time_range,score +200,Electronic,week,1.0 +200,Electronic,allTime,5.0 +201,Alternative,week,2.0 +202,Alternative,week,2.0 +300,Electronic,week,3.0 +300,Electronic,allTime,3.0 \ No newline at end of file diff --git a/api/v1_trending.go b/api/v1_trending.go new file mode 100644 index 00000000..82c3651f --- /dev/null +++ b/api/v1_trending.go @@ -0,0 +1,62 @@ +package api + +import ( + "bridgerton.audius.co/api/dbv1" + "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v5" +) + +func (app *ApiServer) v1Trending(c *fiber.Ctx) error { + myId := c.Locals("myId") + + // SQL query with conditional genre filter + sql := ` + SELECT track_trending_scores.track_id + FROM track_trending_scores + LEFT JOIN tracks + ON tracks.track_id = track_trending_scores.track_id + AND tracks.is_delete = false + AND tracks.is_unlisted = false + AND tracks.is_available = true + WHERE type = 'TRACKS' + AND version = 'pnagD' + AND time_range = @timeRange + AND (@genre = '' OR track_trending_scores.genre = @genre) + ORDER BY + score DESC, + track_id DESC + LIMIT @limit + OFFSET @offset + ` + + args := pgx.NamedArgs{} + args["limit"] = c.Query("limit", "100") + args["offset"] = c.Query("offset", "0") + args["timeRange"] = c.Query("timeRange", "week") + args["genre"] = c.Query("genre", "") + + rows, err := app.pool.Query(c.Context(), sql, args) + if err != nil { + return err + } + + type trackTrendingRow struct { + TrackId int32 `json:"track_id"` + } + + trackIds, err := pgx.CollectRows(rows, pgx.RowTo[int32]) + if err != nil { + return err + } + + tracks, err := app.queries.FullTracks(c.Context(), dbv1.GetTracksParams{ + Ids: trackIds, + MyID: myId, + }) + + if err != nil { + return err + } + + return v1TracksResponse(c, tracks) +} diff --git a/api/v1_trending_test.go b/api/v1_trending_test.go new file mode 100644 index 00000000..a048dc89 --- /dev/null +++ b/api/v1_trending_test.go @@ -0,0 +1,54 @@ +package api + +import ( + "testing" + + "bridgerton.audius.co/api/dbv1" + "bridgerton.audius.co/trashid" + "github.com/stretchr/testify/assert" +) + +func TestGetTrending(t *testing.T) { + var resp struct { + Data []dbv1.FullTrack + } + status, _ := testGet(t, "/v1/tracks/trending", &resp) + assert.Equal(t, 200, status) + + assert.Equal(t, trashid.MustEncodeHashID(300), resp.Data[0].ID) + assert.Equal(t, "Electronic", resp.Data[0].Genre.String) + + assert.Equal(t, trashid.MustEncodeHashID(202), resp.Data[1].ID) + assert.Equal(t, "Alternative", resp.Data[1].Genre.String) + + assert.Equal(t, trashid.MustEncodeHashID(200), resp.Data[2].ID) + assert.Equal(t, "Electronic", resp.Data[2].Genre.String) +} + +func TestGetTrendingElectronic(t *testing.T) { + var resp struct { + Data []dbv1.FullTrack + } + status, _ := testGet(t, "/v1/tracks/trending?genre=Electronic", &resp) + assert.Equal(t, 200, status) + + assert.Equal(t, "eYRWn", resp.Data[0].ID) + assert.Equal(t, "Electronic", resp.Data[0].Genre.String) + + assert.Equal(t, "eYJyn", resp.Data[1].ID) + assert.Equal(t, "Electronic", resp.Data[1].Genre.String) +} + +func TestGetTrendingAllTime(t *testing.T) { + var resp struct { + Data []dbv1.FullTrack + } + status, _ := testGet(t, "/v1/tracks/trending?timeRange=allTime", &resp) + assert.Equal(t, 200, status) + + assert.Equal(t, "eYJyn", resp.Data[0].ID) + assert.Equal(t, "Electronic", resp.Data[0].Genre.String) + + assert.Equal(t, "eYRWn", resp.Data[1].ID) + assert.Equal(t, "Electronic", resp.Data[1].Genre.String) +}