Skip to content

Commit

Permalink
favorite tracks count
Browse files Browse the repository at this point in the history
  • Loading branch information
bocharovatd committed Dec 19, 2024
1 parent 601eef7 commit 18dc859
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 4 deletions.
1 change: 1 addition & 0 deletions microservices/track/delivery.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Handlers interface {
DeleteFavoriteTrack(response http.ResponseWriter, request *http.Request)
IsFavoriteTrack(response http.ResponseWriter, request *http.Request)
GetFavoriteTracks(response http.ResponseWriter, request *http.Request)
GetFavoriteTracksCount(response http.ResponseWriter, request *http.Request)
GetTracksFromPlaylist(response http.ResponseWriter, request *http.Request)
GetPopular(response http.ResponseWriter, request *http.Request)
}
38 changes: 38 additions & 0 deletions microservices/track/delivery/http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,44 @@ func (handlers *trackHandlers) GetFavoriteTracks(response http.ResponseWriter, r
}
}

func (handlers *trackHandlers) GetFavoriteTracksCount(response http.ResponseWriter, request *http.Request) {
requestID := request.Context().Value(utils.RequestIDKey{})
vars := mux.Vars(request)
userID, err := uuid.Parse(vars["userID"])
if err != nil {
handlers.logger.Error(fmt.Sprintf("Get '%s' wrong user id: %v", vars["userID"], err), requestID)
utils.JSONError(response, http.StatusBadRequest, "Wrong id value")
return
}

count, err := handlers.usecase.GetFavoriteTracksCount(request.Context(), userID)
if err != nil {
handlers.logger.Error(fmt.Sprintf("Failed to get favorite tracks count: %v", err), requestID)
utils.JSONError(response, http.StatusInternalServerError, fmt.Sprintf("Failed to get favorite tracks count: %v", err))
return
} else if count == 0 {
utils.JSONError(response, http.StatusNotFound, "No favorite tracks were found")
return
}

response.Header().Set("Content-Type", "application/json")
countResponse := &utils.CountResponse{Count: count}
rawBytes, err := easyjson.Marshal(countResponse)
if err != nil {
handlers.logger.Error(fmt.Sprintf("Failed to encode favorite tracks count: %v", err), requestID)
utils.JSONError(response, http.StatusInternalServerError, fmt.Sprintf("Failed to encode favorite tracks count: %v", err))
return
}

response.WriteHeader(http.StatusOK)
_, err = response.Write(rawBytes)
if err != nil {
handlers.logger.Error(fmt.Sprintf("Failed to write response: %v", err), requestID)
utils.JSONError(response, http.StatusInternalServerError, "Write response fail")
return
}
}

func (handlers *trackHandlers) GetTracksFromPlaylist(response http.ResponseWriter, request *http.Request) {
requestID := request.Context().Value(utils.RequestIDKey{})
vars := mux.Vars(request)
Expand Down
82 changes: 82 additions & 0 deletions microservices/track/delivery/http/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,88 @@ func TestTrackHandlers_GetFavoriteTracks(t *testing.T) {
})
}

func TestTrackHandlers_GetFavoriteTracksCount(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cfg := &config.Config{}
logger := logger.New(&cfg.Service.Logger)
usecaseMock := mocks.NewMockUsecase(ctrl)
trackHandlers := NewTrackHandlers(usecaseMock, logger)

t.Run("Success", func(t *testing.T) {
userID := uuid.New()
count := uint64(2)

usecaseMock.EXPECT().GetFavoriteTracksCount(gomock.Any(), userID).Return(count, nil)

router := mux.NewRouter()
router.HandleFunc("/api/v1/tracks/favorite/count/{userID:[0-9a-fA-F-]+}", trackHandlers.GetFavoriteTracksCount).Methods("GET")

request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/tracks/favorite/count/%s", userID.String()), nil)
assert.NoError(t, err)

response := httptest.NewRecorder()
router.ServeHTTP(response, request)

res := response.Result()
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Contains(t, response.Body.String(), fmt.Sprintf(`"count":%d`, count))
})

t.Run("Wrong id value", func(t *testing.T) {
router := mux.NewRouter()
router.HandleFunc("/api/v1/tracks/favorite/count/{userID:[0-9a-fA-F-]+}", trackHandlers.GetFavoriteTracksCount).Methods("GET")

request, err := http.NewRequest(http.MethodGet, "/api/v1/tracks/favorite/count/123", nil)
assert.NoError(t, err)

response := httptest.NewRecorder()
router.ServeHTTP(response, request)

res := response.Result()
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
assert.Contains(t, response.Body.String(), "Wrong id value")
})

t.Run("Error while getting favorite tracks count", func(t *testing.T) {
userID := uuid.New()
mockError := fmt.Errorf("usecase error")
usecaseMock.EXPECT().GetFavoriteTracksCount(gomock.Any(), userID).Return(uint64(0), mockError)

router := mux.NewRouter()
router.HandleFunc("/api/v1/tracks/favorite/count/{userID:[0-9a-fA-F-]+}", trackHandlers.GetFavoriteTracksCount).Methods("GET")

request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/tracks/favorite/count/%s", userID.String()), nil)
assert.NoError(t, err)

response := httptest.NewRecorder()
router.ServeHTTP(response, request)

res := response.Result()
assert.Equal(t, http.StatusInternalServerError, res.StatusCode)
assert.Contains(t, response.Body.String(), "Failed to get favorite tracks count")
})

t.Run("No favorite tracks found", func(t *testing.T) {
userID := uuid.New()
usecaseMock.EXPECT().GetFavoriteTracksCount(gomock.Any(), userID).Return(uint64(0), nil)

router := mux.NewRouter()
router.HandleFunc("/api/v1/tracks/favorite/count/{userID:[0-9a-fA-F-]+}", trackHandlers.GetFavoriteTracksCount).Methods("GET")

request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/tracks/favorite/count/%s", userID.String()), nil)
assert.NoError(t, err)

response := httptest.NewRecorder()
router.ServeHTTP(response, request)

res := response.Result()
assert.Equal(t, http.StatusNotFound, res.StatusCode)
assert.Contains(t, response.Body.String(), "No favorite tracks were found")
})
}

func createTrackRequestWithVars(requestID string, userID uuid.UUID) *http.Request {
request := httptest.NewRequest(http.MethodGet, "/api/v1/tracks/favorite/byUser/"+userID.String(), nil)
request = request.WithContext(context.WithValue(request.Context(), utils.RequestIDKey{}, requestID))
Expand Down
5 changes: 5 additions & 0 deletions microservices/track/delivery/http/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ func BindRoutes(s *httpServer.Server, artistClient artistService.ArtistServiceCl
middleware.AuthMiddleware(&s.CFG.Service.Auth, s.Logger, http.HandlerFunc(trackHandleres.GetFavoriteTracks)),
).Methods("GET")

s.MUX.Handle(
"/api/v1/tracks/favorite/count/{userID:[0-9a-fA-F-]+}",
middleware.AuthMiddleware(&s.CFG.Service.Auth, s.Logger, http.HandlerFunc(trackHandleres.GetFavoriteTracksCount)),
).Methods("GET")

s.MUX.Handle(
"/api/v1/tracks/favorite/{trackID:[0-9]+}",
middleware.AuthMiddleware(&s.CFG.Service.Auth, s.Logger, http.HandlerFunc(trackHandleres.IsFavoriteTrack)),
Expand Down
19 changes: 17 additions & 2 deletions microservices/track/mock/repository_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 17 additions & 2 deletions microservices/track/mock/usecase_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions microservices/track/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Repo interface {
DeleteFavoriteTrack(ctx context.Context, userID uuid.UUID, trackID uint64) error
IsFavoriteTrack(ctx context.Context, userID uuid.UUID, trackID uint64) (bool, error)
GetFavoriteTracks(ctx context.Context, userID uuid.UUID) ([]*models.Track, error)
GetFavoriteTracksCount(ctx context.Context, userID uuid.UUID) (uint64, error)
GetTracksFromPlaylist(ctx context.Context, playlistID uint64) ([]*models.PlaylistTrack, error)
GetPopular(ctx context.Context) ([]*models.Track, error)
}
10 changes: 10 additions & 0 deletions microservices/track/repository/pg_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,16 @@ func (r *TrackRepository) GetFavoriteTracks(ctx context.Context, userID uuid.UUI
return tracks, nil
}

func (r *TrackRepository) GetFavoriteTracksCount(ctx context.Context, userID uuid.UUID) (uint64, error) {
var count uint64
err := r.db.QueryRowContext(ctx, getFavoriteCountQuery, userID).Scan(&count)
if err != nil && err != sql.ErrNoRows {
return 0, errors.Wrap(err, "GetFavoriteTracksCount.Query")
}

return count, nil
}

func (r *TrackRepository) GetTracksFromPlaylist(ctx context.Context, playlistID uint64) ([]*models.PlaylistTrack, error) {
playlist := []*models.PlaylistTrack{}
rows, err := r.db.QueryContext(ctx,
Expand Down
22 changes: 22 additions & 0 deletions microservices/track/repository/pg_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,28 @@ func TestTrackRepositoryGetFavoriteTracks(t *testing.T) {
require.Equal(t, foundTracks, expectedTracks)
}

func TestTrackRepositoryGetFavoriteTracksCount(t *testing.T) {
t.Parallel()

db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()

trackRepository := NewTrackPGRepository(db)

userID := uuid.New()
expectedCount := uint64(7)

mock.ExpectQuery(getFavoriteCountQuery).WithArgs(userID).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(expectedCount))

count, err := trackRepository.GetFavoriteTracksCount(context.Background(), userID)
require.NoError(t, err)
require.Equal(t, expectedCount, count)

err = mock.ExpectationsWereMet()
require.NoError(t, err)
}

func TestTrackRepositoryGetPopular(t *testing.T) {
t.Parallel()

Expand Down
7 changes: 7 additions & 0 deletions microservices/track/repository/sql_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ const (
ON t.id = ft.track_id
WHERE ft.user_id = $1`

getFavoriteCountQuery = `
SELECT COUNT(*)
FROM track AS a
JOIN favorite_track AS fa
ON a.id = fa.track_id
WHERE fa.user_id = $1`

getTracksFromPlaylistQuery = `SELECT id, playlist_id, track_order_in_playlist, track_id, created_at FROM playlist_track WHERE playlist_id = $1 ORDER BY created_at DESC`

getPopularTracksQuery = `SELECT
Expand Down
1 change: 1 addition & 0 deletions microservices/track/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Usecase interface {
DeleteFavoriteTrack(ctx context.Context, userID uuid.UUID, trackID uint64) error
IsFavoriteTrack(ctx context.Context, userID uuid.UUID, trackID uint64) (bool, error)
GetFavoriteTracks(ctx context.Context, userID uuid.UUID) ([]*dto.TrackDTO, error)
GetFavoriteTracksCount(ctx context.Context, userID uuid.UUID) (uint64, error)
ConvertTrackToDTO(ctx context.Context, track *models.Track) (*dto.TrackDTO, error)
GetTracksFromPlaylist(ctx context.Context, playlistID uint64) ([]*dto.TrackDTO, error)
GetPopular(ctx context.Context) ([]*dto.TrackDTO, error)
Expand Down
12 changes: 12 additions & 0 deletions microservices/track/usecase/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ func (usecase *trackUsecase) GetFavoriteTracks(ctx context.Context, userID uuid.
return dtoTracks, nil
}

func (usecase *trackUsecase) GetFavoriteTracksCount(ctx context.Context, userID uuid.UUID) (uint64, error) {
requestID := ctx.Value(utils.RequestIDKey{})
count, err := usecase.trackRepo.GetFavoriteTracksCount(ctx, userID)
if err != nil {
usecase.logger.Warn(fmt.Sprintf("Can't load favorite tracks count by user ID %v: %v", userID, err), requestID)
return 0, fmt.Errorf("Can't load tracks by user ID %v", userID)
}
usecase.logger.Infof("Found %d favorite tracks for user ID %v", count, userID)

return count, nil
}

func (u *trackUsecase) GetTracksFromPlaylist(ctx context.Context, playlistID uint64) ([]*dto.TrackDTO, error) {
tracks := []*models.Track{}
playlistTracks, err := u.trackRepo.GetTracksFromPlaylist(ctx, playlistID)
Expand Down
65 changes: 65 additions & 0 deletions microservices/track/usecase/usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,71 @@ func TestUsecase_GetFavoriteTracks_NotFoundTracks(t *testing.T) {
require.EqualError(t, err, fmt.Sprintf("Can't load tracks by user ID %v", userID))
}

func TestUsecase_GetFavoriteTracksCount_FoundTracks(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
defer ctrl.Finish()

cfg := &config.Config{
Service: config.ServiceConfig{
Logger: config.LoggerConfig{
Level: "info",
Format: "json",
},
},
}

logger := logger.New(&cfg.Service.Logger)
trackRepoMock := mockTrack.NewMockRepo(ctrl)
artistClientMock := mockArtist.NewMockArtistServiceClient(ctrl)
albumClientMock := mockAlbum.NewMockAlbumServiceClient(ctrl)
trackUsecase := NewTrackUsecase(trackRepoMock, artistClientMock, albumClientMock, logger)

userID := uuid.New()
ctx := context.Background()
expectedCount := uint64(10)
trackRepoMock.EXPECT().GetFavoriteTracksCount(ctx, userID).Return(expectedCount, nil)

count, err := trackUsecase.GetFavoriteTracksCount(ctx, userID)

require.NoError(t, err)
require.Equal(t, expectedCount, count)
}

func TestUsecase_GetFavoriteTracksCount_ErrorGettingCount(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
defer ctrl.Finish()

cfg := &config.Config{
Service: config.ServiceConfig{
Logger: config.LoggerConfig{
Level: "info",
Format: "json",
},
},
}

logger := logger.New(&cfg.Service.Logger)
trackRepoMock := mockTrack.NewMockRepo(ctrl)
artistClientMock := mockArtist.NewMockArtistServiceClient(ctrl)
albumClientMock := mockAlbum.NewMockAlbumServiceClient(ctrl)
trackUsecase := NewTrackUsecase(trackRepoMock, artistClientMock, albumClientMock, logger)

userID := uuid.New()
ctx := context.Background()
expectedError := fmt.Errorf("Can't load tracks by user ID %v", userID)
trackRepoMock.EXPECT().GetFavoriteTracksCount(ctx, userID).Return(uint64(0), expectedError)

count, err := trackUsecase.GetFavoriteTracksCount(ctx, userID)

require.Error(t, err)
require.EqualError(t, err, expectedError.Error())
require.Equal(t, uint64(0), count)
}

func TestUsecase_GetTracksFromPlaylist_FoundTracks(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 18dc859

Please sign in to comment.