From 8b46986c81962b0b35c5d075771be705fe01981f Mon Sep 17 00:00:00 2001 From: Divaldoh Date: Mon, 22 Jul 2024 00:02:12 -0300 Subject: [PATCH] function that search the data of specific track --- .../handlers/dtos/response/song_response.go | 104 ++-- .../api/endpoints/handlers/user_handlers.go | 468 ++++++++++-------- .../api/endpoints/routes/user/user_routes.go | 41 +- src/core/domain/song/builder.go | 256 +++++----- src/core/domain/song/song.go | 110 ++-- src/core/interfaces/primary/user_manager.go | 35 +- src/core/interfaces/repository/user_loader.go | 35 +- src/core/services/user_services.go | 149 +++--- src/infra/spotify/user_spotify_repository.go | 47 ++ 9 files changed, 675 insertions(+), 570 deletions(-) diff --git a/src/app/api/endpoints/handlers/dtos/response/song_response.go b/src/app/api/endpoints/handlers/dtos/response/song_response.go index fbb9c83..8f1d027 100644 --- a/src/app/api/endpoints/handlers/dtos/response/song_response.go +++ b/src/app/api/endpoints/handlers/dtos/response/song_response.go @@ -1,52 +1,52 @@ -package response - -import ( - "time" - - "github.com/google/uuid" -) - -type SongDTO struct { - ID string `json:"id"` - Name string `json:"name"` - Artists []ArtistDTO `json:"artists"` - AlbumID *uuid.UUID `json:"album_id,omitempty"` - ReleaseDate time.Time `json:"release_date,omitempty"` - Duration int `json:"duration"` -} - -func NewSongDTO(ID, Name string, Artists []ArtistDTO, AlbumID *uuid.UUID, ReleaseDate time.Time, Duration int) *SongDTO { - return &SongDTO{ - ID: ID, - Name: Name, - Artists: Artists, - AlbumID: AlbumID, - ReleaseDate: ReleaseDate, - Duration: Duration, - } -} - -type SongHighDTO struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - AlbumID *uuid.UUID `json:"album_id,omitempty"` - ReleaseDate time.Time `json:"release_date"` - Duration string `json:"duration"` - Lyrics *string `json:"lyrics"` - TrackNumber *int `json:"track_number"` - SpotifyURL *string `json:"spotify_url"` -} - -func NewSongHighDTO(ID uuid.UUID, Name string, AlbumID *uuid.UUID, - ReleaseDate time.Time, Duration string, Lyrics, SpotifyURL *string, TrackNumber *int) *SongHighDTO { - return &SongHighDTO{ - ID: ID, - Name: Name, - AlbumID: AlbumID, - ReleaseDate: ReleaseDate, - Duration: Duration, - Lyrics: Lyrics, - TrackNumber: TrackNumber, - SpotifyURL: SpotifyURL, - } -} +package response + +import ( + "time" + + "github.com/google/uuid" +) + +type SongDTO struct { + ID string `json:"id"` + Name string `json:"name"` + Artists []ArtistDTO `json:"artists"` + AlbumID string `json:"album_id,omitempty"` + ReleaseDate time.Time `json:"release_date,omitempty"` + Duration int `json:"duration"` +} + +func NewSongDTO(ID, Name string, Artists []ArtistDTO, AlbumID string, ReleaseDate time.Time, Duration int) *SongDTO { + return &SongDTO{ + ID: ID, + Name: Name, + Artists: Artists, + AlbumID: AlbumID, + ReleaseDate: ReleaseDate, + Duration: Duration, + } +} + +type SongHighDTO struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + AlbumID *uuid.UUID `json:"album_id,omitempty"` + ReleaseDate time.Time `json:"release_date"` + Duration string `json:"duration"` + Lyrics *string `json:"lyrics"` + TrackNumber *int `json:"track_number"` + SpotifyURL *string `json:"spotify_url"` +} + +func NewSongHighDTO(ID uuid.UUID, Name string, AlbumID *uuid.UUID, + ReleaseDate time.Time, Duration string, Lyrics, SpotifyURL *string, TrackNumber *int) *SongHighDTO { + return &SongHighDTO{ + ID: ID, + Name: Name, + AlbumID: AlbumID, + ReleaseDate: ReleaseDate, + Duration: Duration, + Lyrics: Lyrics, + TrackNumber: TrackNumber, + SpotifyURL: SpotifyURL, + } +} diff --git a/src/app/api/endpoints/handlers/user_handlers.go b/src/app/api/endpoints/handlers/user_handlers.go index f64bd1a..d029f00 100644 --- a/src/app/api/endpoints/handlers/user_handlers.go +++ b/src/app/api/endpoints/handlers/user_handlers.go @@ -1,209 +1,259 @@ -package handlers - -import ( - "echofy_backend/src/app/api/endpoints/handlers/dtos/response" - "echofy_backend/src/core/interfaces/primary" - "net/http" - - "github.com/labstack/echo/v4" -) - -const ( - albumID = "albumID" - artistID = "artistID" - songID = "songID" - playlistID = "playlistID" -) - -type UserHandlers struct { - service primary.UserManager -} - -// GetSongsByPlaylistID -// @ID GetSongsByPlaylistID -// @Summary Buscar todas as músicas de uma playlist -// @Tags Rotas do usuário -// @Description Rota que permite que se busque todas as músicas de uma determinada playlist -// @Param playlistID path string true "ID da playlist." default(7pCvSVfjcnOw6AFJNZZ4bN) -// @Produce json -// @Success 200 {array} response.SongDTO "Requisição realizada com sucesso." -// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." -// @Failure 403 {object} response.ErrorMessage "Acesso negado." -// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." -// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." -// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." -// @Router /user/playlist/{playlistID}/songs [get] -func (h UserHandlers) GetSongsByPlaylistID(context echo.Context) error { - playlistID := context.Param(playlistID) - - songRows, fetchErr := h.service.FetchSongsByPlaylistID(playlistID) - if fetchErr != nil { - return getHttpHandledErrorResponse(context, fetchErr) - } - - songs := make([]response.SongDTO, 0) - for _, each := range songRows { - artists := make([]response.ArtistDTO, 0) - for _, eachArtist := range each.Artists() { - artistBuilder := response.NewArtistWithLowDataDTO( - eachArtist.ID(), - eachArtist.Name(), - *eachArtist.SpotifyURL(), - ) - - artists = append(artists, *artistBuilder) - } - songBuilder := response.NewSongDTO( - each.ID(), - each.Name(), - artists, - each.AlbumID(), - each.ReleaseDate(), - each.Duration(), - ) - songs = append(songs, *songBuilder) - } - - return context.JSON(http.StatusOK, songs) -} - -// GetPlaylistID -// @ID GetPlaylistID -// @Summary Buscar os dados de uma playlist -// @Tags Rotas do usuário -// @Description Rota que permite que se busque todas as informações de uma playlist -// @Param playlistID path string true "ID da playlist." default(7pCvSVfjcnOw6AFJNZZ4bN) -// @Produce json -// @Success 200 {object} response.PlaylistDTO "Requisição realizada com sucesso." -// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." -// @Failure 403 {object} response.ErrorMessage "Acesso negado." -// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." -// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." -// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." -// @Router /user/playlist/{playlistID} [get] -func (h UserHandlers) GetPlaylistByID(context echo.Context) error { - playlistID := context.Param(playlistID) - - playlistRow, fetchErr := h.service.FetchPlaylistByID(playlistID) - if fetchErr != nil { - return getHttpHandledErrorResponse(context, fetchErr) - } - - playlist := response.NewPlaylistDTO(*playlistRow) - - return context.JSON(http.StatusOK, playlist) -} - -// GetAlbumTracks -// @ID GetAlbumTracks -// @Summary Buscar todas as músicas de um Álbum -// @Tags Rotas do usuário -// @Description Rota que permite que se busque todas as músicas de uma determinado Álbum -// @Param albumID path string true "ID do Álbum." default(3WFTGIO6E3Xh4paEOBY9OU) -// @Produce json -// @Success 200 {array} response.SongDTO "Requisição realizada com sucesso." -// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." -// @Failure 403 {object} response.ErrorMessage "Acesso negado." -// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." -// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." -// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." -// @Router /user/album/{albumID}/songs [get] -func (h UserHandlers) GetAlbumTracks(context echo.Context) error { - albumID := context.Param(albumID) - - songRows, fetchErr := h.service.FetchSongsByAlbumID(albumID) - if fetchErr != nil { - return getHttpHandledErrorResponse(context, fetchErr) - } - - songs := make([]response.SongDTO, 0) - for _, each := range songRows { - artists := make([]response.ArtistDTO, 0) - for _, eachArtist := range each.Artists() { - artistBuilder := response.NewArtistWithLowDataDTO( - eachArtist.ID(), - eachArtist.Name(), - *eachArtist.SpotifyURL(), - ) - - artists = append(artists, *artistBuilder) - } - songBuilder := response.NewSongDTO( - each.ID(), - each.Name(), - artists, - each.AlbumID(), - each.ReleaseDate(), - each.Duration(), - ) - songs = append(songs, *songBuilder) - } - - return context.JSON(http.StatusOK, songs) -} - -// GetAlbumByArtistID -// @ID GetAlbumByArtistID -// @Summary Buscar todas os álbuns de um artista pelo seu ID -// @Tags Rotas do usuário -// @Description Rota que permite que se busque todos os álbuns de um determinado artista -// @Param artistID path string true "ID do Artista." default(5K4W6rqBFWDnAN6FQUkS6x) -// @Produce json -// @Success 200 {array} response.AlbumDTO "Requisição realizada com sucesso." -// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." -// @Failure 403 {object} response.ErrorMessage "Acesso negado." -// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." -// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." -// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." -// @Router /user/artist/{artistID}/albums [get] -func (h UserHandlers) GetArtistAlbums(context echo.Context) error { - artistID := context.Param(artistID) - - albumsRows, fetchErr := h.service.FetchArtistAlbumsByID(artistID) - if fetchErr != nil { - return getHttpHandledErrorResponse(context, fetchErr) - } - - albums := make([]response.AlbumDTO, 0) - for _, each := range albumsRows { - albumBuilder := response.NewAlbumDTO( - each.ID(), - each.Name(), - each.ArtistID(), - each.ReleaseDate(), - each.Description(), - each.ImageURL(), - ) - albums = append(albums, *albumBuilder) - - } - - return context.JSON(http.StatusOK, albums) - -} - -// GetUserBasicInfo -// @ID GetUserBasicInfo -// @Summary Buscar alguns dados pessoais do usuario -// @Tags Rotas do usuário -// @Description Rota que permite que se busque alguns dados do usuário autenticado -// @Produce json -// @Success 200 {array} response.UserDTO "Requisição realizada com sucesso." -// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." -// @Failure 403 {object} response.ErrorMessage "Acesso negado." -// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." -// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." -// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." -// @Router /user [get] -func (h UserHandlers) GetUserBasicInfo(context echo.Context) error { - userRow, fetchErr := h.service.FetchUserBasicInfo() - if fetchErr != nil { - return getHttpHandledErrorResponse(context, fetchErr) - } - user := response.NewUserDTO(*userRow) - return context.JSON(http.StatusOK, user) -} - -func NewUserHandlers(service primary.UserManager) *UserHandlers { - return &UserHandlers{service: service} -} +package handlers + +import ( + "echofy_backend/src/app/api/endpoints/handlers/dtos/response" + "echofy_backend/src/core/interfaces/primary" + "net/http" + "time" + + "github.com/labstack/echo/v4" +) + +const ( + albumID = "albumID" + artistID = "artistID" + songID = "songID" + playlistID = "playlistID" +) + +type UserHandlers struct { + service primary.UserManager +} + +// GetSongsByPlaylistID +// @ID GetSongsByPlaylistID +// @Summary Buscar todas as músicas de uma playlist +// @Tags Rotas do usuário +// @Description Rota que permite que se busque todas as músicas de uma determinada playlist +// @Param playlistID path string true "ID da playlist." default(7pCvSVfjcnOw6AFJNZZ4bN) +// @Produce json +// @Success 200 {array} response.SongDTO "Requisição realizada com sucesso." +// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." +// @Failure 403 {object} response.ErrorMessage "Acesso negado." +// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." +// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." +// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." +// @Router /user/playlist/{playlistID}/songs [get] +func (h UserHandlers) GetSongsByPlaylistID(context echo.Context) error { + playlistID := context.Param(playlistID) + + songRows, fetchErr := h.service.FetchSongsByPlaylistID(playlistID) + if fetchErr != nil { + return getHttpHandledErrorResponse(context, fetchErr) + } + + songs := make([]response.SongDTO, 0) + for _, each := range songRows { + artists := make([]response.ArtistDTO, 0) + for _, eachArtist := range each.Artists() { + artistBuilder := response.NewArtistWithLowDataDTO( + eachArtist.ID(), + eachArtist.Name(), + *eachArtist.SpotifyURL(), + ) + + artists = append(artists, *artistBuilder) + } + songBuilder := response.NewSongDTO( + each.ID(), + each.Name(), + artists, + each.AlbumID(), + each.ReleaseDate(), + each.Duration(), + ) + songs = append(songs, *songBuilder) + } + + return context.JSON(http.StatusOK, songs) +} + +// GetPlaylistID +// @ID GetPlaylistID +// @Summary Buscar os dados de uma playlist +// @Tags Rotas do usuário +// @Description Rota que permite que se busque todas as informações de uma playlist +// @Param playlistID path string true "ID da playlist." default(7pCvSVfjcnOw6AFJNZZ4bN) +// @Produce json +// @Success 200 {object} response.PlaylistDTO "Requisição realizada com sucesso." +// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." +// @Failure 403 {object} response.ErrorMessage "Acesso negado." +// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." +// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." +// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." +// @Router /user/playlist/{playlistID} [get] +func (h UserHandlers) GetPlaylistByID(context echo.Context) error { + playlistID := context.Param(playlistID) + + playlistRow, fetchErr := h.service.FetchPlaylistByID(playlistID) + if fetchErr != nil { + return getHttpHandledErrorResponse(context, fetchErr) + } + + playlist := response.NewPlaylistDTO(*playlistRow) + + return context.JSON(http.StatusOK, playlist) +} + +// GetAlbumTracks +// @ID GetAlbumTracks +// @Summary Buscar todas as músicas de um Álbum +// @Tags Rotas do usuário +// @Description Rota que permite que se busque todas as músicas de uma determinado Álbum +// @Param albumID path string true "ID do Álbum." default(3WFTGIO6E3Xh4paEOBY9OU) +// @Produce json +// @Success 200 {array} response.SongDTO "Requisição realizada com sucesso." +// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." +// @Failure 403 {object} response.ErrorMessage "Acesso negado." +// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." +// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." +// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." +// @Router /user/album/{albumID}/songs [get] +func (h UserHandlers) GetAlbumTracks(context echo.Context) error { + albumID := context.Param(albumID) + + songRows, fetchErr := h.service.FetchSongsByAlbumID(albumID) + if fetchErr != nil { + return getHttpHandledErrorResponse(context, fetchErr) + } + + songs := make([]response.SongDTO, 0) + for _, each := range songRows { + artists := make([]response.ArtistDTO, 0) + for _, eachArtist := range each.Artists() { + artistBuilder := response.NewArtistWithLowDataDTO( + eachArtist.ID(), + eachArtist.Name(), + *eachArtist.SpotifyURL(), + ) + + artists = append(artists, *artistBuilder) + } + songBuilder := response.NewSongDTO( + each.ID(), + each.Name(), + artists, + each.AlbumID(), + each.ReleaseDate(), + each.Duration(), + ) + songs = append(songs, *songBuilder) + } + + return context.JSON(http.StatusOK, songs) +} + +// GetAlbumByArtistID +// @ID GetAlbumByArtistID +// @Summary Buscar todas os álbuns de um artista pelo seu ID +// @Tags Rotas do usuário +// @Description Rota que permite que se busque todos os álbuns de um determinado artista +// @Param artistID path string true "ID do Artista." default(5K4W6rqBFWDnAN6FQUkS6x) +// @Produce json +// @Success 200 {array} response.AlbumDTO "Requisição realizada com sucesso." +// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." +// @Failure 403 {object} response.ErrorMessage "Acesso negado." +// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." +// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." +// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." +// @Router /user/artist/{artistID}/albums [get] +func (h UserHandlers) GetArtistAlbums(context echo.Context) error { + artistID := context.Param(artistID) + + albumsRows, fetchErr := h.service.FetchArtistAlbumsByID(artistID) + if fetchErr != nil { + return getHttpHandledErrorResponse(context, fetchErr) + } + + albums := make([]response.AlbumDTO, 0) + for _, each := range albumsRows { + albumBuilder := response.NewAlbumDTO( + each.ID(), + each.Name(), + each.ArtistID(), + each.ReleaseDate(), + each.Description(), + each.ImageURL(), + ) + albums = append(albums, *albumBuilder) + + } + + return context.JSON(http.StatusOK, albums) + +} + +// GetSongDetails +// @ID GetSongDetails +// @Summary Buscar dados de uma música especifíca +// @Tags Rotas do usuário +// @Description Rota que permite que se busque os dados de uma música +// @Param songID path string true "ID da música." default(1SKPmfSYaPsETbRHaiA18G) +// @Produce json +// @Success 200 {array} response.SongDTO "Requisição realizada com sucesso." +// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." +// @Failure 403 {object} response.ErrorMessage "Acesso negado." +// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." +// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." +// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." +// @Router /user/song/{songID}/details [get] +func (h UserHandlers) FetchSongDetailsByID(context echo.Context) error { + songID := context.Param("songID") + + songRows, fetchErr := h.service.FetchSongDetailsByID(songID) + if fetchErr != nil { + return getHttpHandledErrorResponse(context, fetchErr) + } + + artists := make([]response.ArtistDTO, 0) + for _, each := range songRows.Artists() { + artistBuilder := response.NewArtistDTO( + each.ID(), + each.Name(), + nil, + nil, + time.Time{}, + nil, + nil, + ) + artists = append(artists, *artistBuilder) + } + + track := response.NewSongDTO( + songRows.ID(), + songRows.Name(), + artists, + songRows.AlbumID(), + songRows.ReleaseDate(), + songRows.Duration(), + ) + + return context.JSON(http.StatusOK, track) + +} + +// GetUserBasicInfo +// @ID GetUserBasicInfo +// @Summary Buscar alguns dados pessoais do usuario +// @Tags Rotas do usuário +// @Description Rota que permite que se busque alguns dados do usuário autenticado +// @Produce json +// @Success 200 {array} response.UserDTO "Requisição realizada com sucesso." +// @Failure 401 {object} response.ErrorMessage "Usuário não autorizado." +// @Failure 403 {object} response.ErrorMessage "Acesso negado." +// @Failure 422 {object} response.ErrorMessage "Algum dado informado não pôde ser processado." +// @Failure 500 {object} response.ErrorMessage "Ocorreu um erro inesperado." +// @Failure 503 {object} response.ErrorMessage "A base de dados não está disponível." +// @Router /user [get] +func (h UserHandlers) GetUserBasicInfo(context echo.Context) error { + userRow, fetchErr := h.service.FetchUserBasicInfo() + if fetchErr != nil { + return getHttpHandledErrorResponse(context, fetchErr) + } + user := response.NewUserDTO(*userRow) + return context.JSON(http.StatusOK, user) +} + +func NewUserHandlers(service primary.UserManager) *UserHandlers { + return &UserHandlers{service: service} +} diff --git a/src/app/api/endpoints/routes/user/user_routes.go b/src/app/api/endpoints/routes/user/user_routes.go index bf5a29c..10c1cb9 100644 --- a/src/app/api/endpoints/routes/user/user_routes.go +++ b/src/app/api/endpoints/routes/user/user_routes.go @@ -1,20 +1,21 @@ -package user - -import ( - "echofy_backend/src/app/api/endpoints/dicontainer" - - "github.com/labstack/echo/v4" -) - -var userHandlers = dicontainer.GetUserHandlers() - -func LoadUserRoutes(group *echo.Group) { - userGroup := group.Group("/user") - userHandlers := dicontainer.GetUserHandlers() - - userGroup.GET("", userHandlers.GetUserBasicInfo) - userGroup.GET("/playlist/:playlistID", userHandlers.GetPlaylistByID) - userGroup.GET("/playlist/:playlistID/songs", userHandlers.GetSongsByPlaylistID) - userGroup.GET("/album/:albumID/songs", userHandlers.GetAlbumTracks) - userGroup.GET("/artist/:artistID/albums", userHandlers.GetArtistAlbums) -} +package user + +import ( + "echofy_backend/src/app/api/endpoints/dicontainer" + + "github.com/labstack/echo/v4" +) + +var userHandlers = dicontainer.GetUserHandlers() + +func LoadUserRoutes(group *echo.Group) { + userGroup := group.Group("/user") + userHandlers := dicontainer.GetUserHandlers() + + userGroup.GET("", userHandlers.GetUserBasicInfo) + userGroup.GET("/playlist/:playlistID", userHandlers.GetPlaylistByID) + userGroup.GET("/playlist/:playlistID/songs", userHandlers.GetSongsByPlaylistID) + userGroup.GET("/album/:albumID/songs", userHandlers.GetAlbumTracks) + userGroup.GET("/artist/:artistID/albums", userHandlers.GetArtistAlbums) + userGroup.GET("/song/:songID/details", userHandlers.FetchSongDetailsByID) +} diff --git a/src/core/domain/song/builder.go b/src/core/domain/song/builder.go index e699179..b9372a7 100644 --- a/src/core/domain/song/builder.go +++ b/src/core/domain/song/builder.go @@ -1,129 +1,127 @@ -package song - -import ( - "echofy_backend/src/core/domain" - "echofy_backend/src/core/domain/artist" - "echofy_backend/src/core/errors" - "echofy_backend/src/core/messages" - "net/url" - "time" - - "github.com/google/uuid" -) - -type Builder struct { - Song - invalidFields []errors.InvalidField -} - -func (b *Builder) WithID(id string) *Builder { - if id == "" { - b.invalidFields = append(b.invalidFields, errors.InvalidField{ - Name: messages.SongID, - Description: messages.SongIDInvalidErrMsg, - }) - } else { - b.id = id - } - return b -} - -func (b *Builder) WithName(name string) *Builder { - if name == "" { - b.invalidFields = append(b.invalidFields, errors.InvalidField{ - Name: messages.SongName, - Description: messages.SongNameInvalidErrMsg, - }) - } else { - b.name = name - } - return b -} - -func (b *Builder) WithArtists(artists []artist.Artist) *Builder { - if artists == nil { - b.invalidFields = append(b.invalidFields, errors.InvalidField{ - Name: messages.SongArtistID, - Description: messages.SongArtistIDInvalidErrMsg, - }) - } else { - b.artists = artists - } - return b -} - -func (b *Builder) WithAlbumID(albumID *uuid.UUID) *Builder { - b.albumID = albumID - return b -} - -func (b *Builder) WithReleaseDate(releaseDate time.Time) *Builder { - if releaseDate.IsZero() { - b.invalidFields = append(b.invalidFields, errors.InvalidField{ - Name: messages.SongReleaseDate, - Description: messages.SongReleaseDateInvalidErrMsg, - }) - } else { - b.releaseDate = releaseDate - } - return b -} - -func (b *Builder) WithDuration(duration int) *Builder { - if duration == 0 { - b.invalidFields = append(b.invalidFields, errors.InvalidField{ - Name: messages.SongDuration, - Description: messages.SongDurationInvalidErrMsg, - }) - } else { - b.duration = duration - } - return b -} - -func (b *Builder) WithLyrics(lyrics string) *Builder { - b.lyrics = &lyrics - return b -} - -// TODO: resolve this two functions - -func (b *Builder) WithTrackNumber(trackNumber int) *Builder { - // if trackNumber <= 0 { - // b.invalidFields = append(b.invalidFields, errors.InvalidField{ - // Name: messages.SongTrackNumber, - // Description: messages.SongTrackNumberInvalidErrMsg, - // }) - // } - b.trackNumber = &trackNumber - return b -} - -func (b *Builder) WithSpotifyURL(spotifyURL string) *Builder { - // if !IsValidURL(spotifyURL) { - // b.invalidFields = append(b.invalidFields, errors.InvalidField{ - // Name: messages.SongSpotifyURL, - // Description: messages.SongSpotifyURLInvalidErrMsg, - // }) - // } - b.spotifyURL = spotifyURL - return b -} - -func (b *Builder) Build() (*Song, errors.Error) { - if len(b.invalidFields) > 0 { - domain.ShowInvalidFields(b.invalidFields) - return nil, errors.NewValidationError(messages.SongBuildErr, b.invalidFields...) - } - - return &b.Song, nil -} - -func IsValidURL(str string) bool { - _, err := url.Parse(str) - return err != nil -} - -func NewBuilder() *Builder { - return &Builder{} -} +package song + +import ( + "echofy_backend/src/core/domain" + "echofy_backend/src/core/domain/artist" + "echofy_backend/src/core/errors" + "echofy_backend/src/core/messages" + "net/url" + "time" +) + +type Builder struct { + Song + invalidFields []errors.InvalidField +} + +func (b *Builder) WithID(id string) *Builder { + if id == "" { + b.invalidFields = append(b.invalidFields, errors.InvalidField{ + Name: messages.SongID, + Description: messages.SongIDInvalidErrMsg, + }) + } else { + b.id = id + } + return b +} + +func (b *Builder) WithName(name string) *Builder { + if name == "" { + b.invalidFields = append(b.invalidFields, errors.InvalidField{ + Name: messages.SongName, + Description: messages.SongNameInvalidErrMsg, + }) + } else { + b.name = name + } + return b +} + +func (b *Builder) WithArtists(artists []artist.Artist) *Builder { + if artists == nil { + b.invalidFields = append(b.invalidFields, errors.InvalidField{ + Name: messages.SongArtistID, + Description: messages.SongArtistIDInvalidErrMsg, + }) + } else { + b.artists = artists + } + return b +} + +func (b *Builder) WithAlbumID(albumID string) *Builder { + b.albumID = albumID + return b +} + +func (b *Builder) WithReleaseDate(releaseDate time.Time) *Builder { + if releaseDate.IsZero() { + b.invalidFields = append(b.invalidFields, errors.InvalidField{ + Name: messages.SongReleaseDate, + Description: messages.SongReleaseDateInvalidErrMsg, + }) + } else { + b.releaseDate = releaseDate + } + return b +} + +func (b *Builder) WithDuration(duration int) *Builder { + if duration == 0 { + b.invalidFields = append(b.invalidFields, errors.InvalidField{ + Name: messages.SongDuration, + Description: messages.SongDurationInvalidErrMsg, + }) + } else { + b.duration = duration + } + return b +} + +func (b *Builder) WithLyrics(lyrics string) *Builder { + b.lyrics = &lyrics + return b +} + +// TODO: resolve this two functions + +func (b *Builder) WithTrackNumber(trackNumber int) *Builder { + // if trackNumber <= 0 { + // b.invalidFields = append(b.invalidFields, errors.InvalidField{ + // Name: messages.SongTrackNumber, + // Description: messages.SongTrackNumberInvalidErrMsg, + // }) + // } + b.trackNumber = &trackNumber + return b +} + +func (b *Builder) WithSpotifyURL(spotifyURL string) *Builder { + // if !IsValidURL(spotifyURL) { + // b.invalidFields = append(b.invalidFields, errors.InvalidField{ + // Name: messages.SongSpotifyURL, + // Description: messages.SongSpotifyURLInvalidErrMsg, + // }) + // } + b.spotifyURL = spotifyURL + return b +} + +func (b *Builder) Build() (*Song, errors.Error) { + if len(b.invalidFields) > 0 { + domain.ShowInvalidFields(b.invalidFields) + return nil, errors.NewValidationError(messages.SongBuildErr, b.invalidFields...) + } + + return &b.Song, nil +} + +func IsValidURL(str string) bool { + _, err := url.Parse(str) + return err != nil +} + +func NewBuilder() *Builder { + return &Builder{} +} diff --git a/src/core/domain/song/song.go b/src/core/domain/song/song.go index 8ec52bb..8b4cc92 100644 --- a/src/core/domain/song/song.go +++ b/src/core/domain/song/song.go @@ -1,56 +1,54 @@ -package song - -import ( - "echofy_backend/src/core/domain/artist" - "time" - - "github.com/google/uuid" -) - -type Song struct { - id string - name string - artists []artist.Artist - albumID *uuid.UUID - releaseDate time.Time - duration int - lyrics *string - trackNumber *int - spotifyURL string -} - -func (s Song) ID() string { - return s.id -} - -func (s Song) Name() string { - return s.name -} - -func (s Song) Artists() []artist.Artist { - return s.artists -} - -func (s Song) AlbumID() *uuid.UUID { - return s.albumID -} - -func (s Song) ReleaseDate() time.Time { - return s.releaseDate -} - -func (s Song) Duration() int { - return s.duration -} - -func (s Song) Lyrics() *string { - return s.lyrics -} - -func (s Song) TrackNumber() *int { - return s.trackNumber -} - -func (s Song) SpotifyURL() string { - return s.spotifyURL -} +package song + +import ( + "echofy_backend/src/core/domain/artist" + "time" +) + +type Song struct { + id string + name string + artists []artist.Artist + albumID string + releaseDate time.Time + duration int + lyrics *string + trackNumber *int + spotifyURL string +} + +func (s Song) ID() string { + return s.id +} + +func (s Song) Name() string { + return s.name +} + +func (s Song) Artists() []artist.Artist { + return s.artists +} + +func (s Song) AlbumID() string { + return s.albumID +} + +func (s Song) ReleaseDate() time.Time { + return s.releaseDate +} + +func (s Song) Duration() int { + return s.duration +} + +func (s Song) Lyrics() *string { + return s.lyrics +} + +func (s Song) TrackNumber() *int { + return s.trackNumber +} + +func (s Song) SpotifyURL() string { + return s.spotifyURL +} diff --git a/src/core/interfaces/primary/user_manager.go b/src/core/interfaces/primary/user_manager.go index 4bdca24..61eaa54 100644 --- a/src/core/interfaces/primary/user_manager.go +++ b/src/core/interfaces/primary/user_manager.go @@ -1,17 +1,18 @@ -package primary - -import ( - "echofy_backend/src/core/domain/album" - "echofy_backend/src/core/domain/playlist" - "echofy_backend/src/core/domain/song" - "echofy_backend/src/core/domain/user" - "echofy_backend/src/core/errors" -) - -type UserManager interface { - FetchSongsByPlaylistID(playlistID string) ([]song.Song, errors.Error) - FetchPlaylistByID(playlistID string) (*playlist.Playlist, errors.Error) - FetchSongsByAlbumID(albumID string) ([]song.Song, errors.Error) - FetchUserBasicInfo() (*user.User, errors.Error) - FetchArtistAlbumsByID(artistID string) ([]album.Album, errors.Error) -} +package primary + +import ( + "echofy_backend/src/core/domain/album" + "echofy_backend/src/core/domain/playlist" + "echofy_backend/src/core/domain/song" + "echofy_backend/src/core/domain/user" + "echofy_backend/src/core/errors" +) + +type UserManager interface { + FetchSongsByPlaylistID(playlistID string) ([]song.Song, errors.Error) + FetchPlaylistByID(playlistID string) (*playlist.Playlist, errors.Error) + FetchSongsByAlbumID(albumID string) ([]song.Song, errors.Error) + FetchUserBasicInfo() (*user.User, errors.Error) + FetchArtistAlbumsByID(artistID string) ([]album.Album, errors.Error) + FetchSongDetailsByID(songID string) (*song.Song, errors.Error) +} diff --git a/src/core/interfaces/repository/user_loader.go b/src/core/interfaces/repository/user_loader.go index dfe23cc..d6b2bbd 100644 --- a/src/core/interfaces/repository/user_loader.go +++ b/src/core/interfaces/repository/user_loader.go @@ -1,17 +1,18 @@ -package repository - -import ( - "echofy_backend/src/core/domain/album" - "echofy_backend/src/core/domain/playlist" - "echofy_backend/src/core/domain/song" - "echofy_backend/src/core/domain/user" - "echofy_backend/src/core/errors" -) - -type UserLoader interface { - FindSongsByPlaylistID(playlistID string) ([]song.Song, errors.Error) - FindPlaylistByID(playlistID string) (*playlist.Playlist, errors.Error) - FindSongsByAlbumID(albumID string) ([]song.Song, errors.Error) - FindUserBasicInfo() (*user.User, errors.Error) - FindArtistAlbumsByID(artistID string) ([]album.Album, errors.Error) -} +package repository + +import ( + "echofy_backend/src/core/domain/album" + "echofy_backend/src/core/domain/playlist" + "echofy_backend/src/core/domain/song" + "echofy_backend/src/core/domain/user" + "echofy_backend/src/core/errors" +) + +type UserLoader interface { + FindSongsByPlaylistID(playlistID string) ([]song.Song, errors.Error) + FindPlaylistByID(playlistID string) (*playlist.Playlist, errors.Error) + FindSongsByAlbumID(albumID string) ([]song.Song, errors.Error) + FindUserBasicInfo() (*user.User, errors.Error) + FindArtistAlbumsByID(artistID string) ([]album.Album, errors.Error) + FindSongDetailsByID(songID string) (*song.Song, errors.Error) +} diff --git a/src/core/services/user_services.go b/src/core/services/user_services.go index 8227a0b..9478983 100644 --- a/src/core/services/user_services.go +++ b/src/core/services/user_services.go @@ -1,70 +1,79 @@ -package services - -import ( - "echofy_backend/src/core/domain/album" - "echofy_backend/src/core/domain/playlist" - "echofy_backend/src/core/domain/song" - "echofy_backend/src/core/domain/user" - "echofy_backend/src/core/errors" - "echofy_backend/src/core/errors/logger" - "echofy_backend/src/core/interfaces/primary" - "echofy_backend/src/core/interfaces/repository" -) - -var _ primary.UserManager = (*UserServices)(nil) - -type UserServices struct { - userRepository repository.UserLoader - logger logger.Logger -} - -func (u UserServices) FetchSongsByPlaylistID(playlistID string) ([]song.Song, errors.Error) { - songs, err := u.userRepository.FindSongsByPlaylistID(playlistID) - if err != nil { - return nil, err - } - - return songs, nil -} - -func (u UserServices) FetchPlaylistByID(playlistID string) (*playlist.Playlist, errors.Error) { - playlist, err := u.userRepository.FindPlaylistByID(playlistID) - if err != nil { - return nil, err - } - - return playlist, nil -} - -func (u UserServices) FetchSongsByAlbumID(albumID string) ([]song.Song, errors.Error) { - album, err := u.userRepository.FindSongsByAlbumID(albumID) - if err != nil { - return nil, err - } - return album, nil -} - -func (u UserServices) FetchUserBasicInfo() (*user.User, errors.Error) { - user, err := u.userRepository.FindUserBasicInfo() - if err != nil { - return nil, err - } - - return user, nil -} - -func (u UserServices) FetchArtistAlbumsByID(artistID string) ([]album.Album, errors.Error) { - albumInstance, err := u.userRepository.FindArtistAlbumsByID(artistID) - if err != nil { - return nil, err - } - - return albumInstance, nil -} - -func NewUserServices(userRepository repository.UserLoader, logger logger.Logger) *UserServices { - return &UserServices{ - userRepository: userRepository, - logger: logger, - } -} +package services + +import ( + "echofy_backend/src/core/domain/album" + "echofy_backend/src/core/domain/playlist" + "echofy_backend/src/core/domain/song" + "echofy_backend/src/core/domain/user" + "echofy_backend/src/core/errors" + "echofy_backend/src/core/errors/logger" + "echofy_backend/src/core/interfaces/primary" + "echofy_backend/src/core/interfaces/repository" +) + +var _ primary.UserManager = (*UserServices)(nil) + +type UserServices struct { + userRepository repository.UserLoader + logger logger.Logger +} + +func (u UserServices) FetchSongsByPlaylistID(playlistID string) ([]song.Song, errors.Error) { + songs, err := u.userRepository.FindSongsByPlaylistID(playlistID) + if err != nil { + return nil, err + } + + return songs, nil +} + +func (u UserServices) FetchPlaylistByID(playlistID string) (*playlist.Playlist, errors.Error) { + playlist, err := u.userRepository.FindPlaylistByID(playlistID) + if err != nil { + return nil, err + } + + return playlist, nil +} + +func (u UserServices) FetchSongsByAlbumID(albumID string) ([]song.Song, errors.Error) { + album, err := u.userRepository.FindSongsByAlbumID(albumID) + if err != nil { + return nil, err + } + return album, nil +} + +func (u UserServices) FetchUserBasicInfo() (*user.User, errors.Error) { + user, err := u.userRepository.FindUserBasicInfo() + if err != nil { + return nil, err + } + + return user, nil +} + +func (u UserServices) FetchArtistAlbumsByID(artistID string) ([]album.Album, errors.Error) { + albumInstance, err := u.userRepository.FindArtistAlbumsByID(artistID) + if err != nil { + return nil, err + } + + return albumInstance, nil +} + +func (u UserServices) FetchSongDetailsByID(songID string) (*song.Song, errors.Error) { + artistIntance, err := u.userRepository.FindSongDetailsByID(songID) + if err != nil { + return nil, err + } + + return artistIntance, nil +} + +func NewUserServices(userRepository repository.UserLoader, logger logger.Logger) *UserServices { + return &UserServices{ + userRepository: userRepository, + logger: logger, + } +} diff --git a/src/infra/spotify/user_spotify_repository.go b/src/infra/spotify/user_spotify_repository.go index 844cb8d..df617af 100644 --- a/src/infra/spotify/user_spotify_repository.go +++ b/src/infra/spotify/user_spotify_repository.go @@ -212,6 +212,53 @@ func (u UserSpotifyRepository) FindArtistAlbumsByID(artistID string) ([]album.Al } +func (u UserSpotifyRepository) FindSongDetailsByID(songID string) (*song.Song, errors.Error) { + ctx := context.Background() + token := getConnection(ctx) + + httpClient := spotifyauth.New().Client(ctx, token) + client := spotify.New(httpClient) + + tracks, err := client.GetTrack(ctx, spotify.ID(songID)) + if err != nil { + return nil, errors.NewUnexpectedError("failed to create Spotify HTTP client", err) + } + + var artists []artist.Artist + + for _, artistSingle := range tracks.Artists { + artistInstance, err := artist.NewBuilder(). + WithName(artistSingle.Name). + WithID(string(artistSingle.ID)). + WithSpotifyURL((*string)(&artistSingle.URI)). + Build() + + if err != nil { + return nil, errors.NewUnexpectedError(messages.ArtistBuildErr, err) + } + + artists = append(artists, *artistInstance) + + } + + trackIntance, err := song.NewBuilder(). + WithAlbumID(string(tracks.Album.ID)). + WithArtists(artists). + WithDuration(tracks.Duration). + WithID(string(tracks.ID)). + WithName(tracks.Name). + WithReleaseDate(tracks.Album.ReleaseDateTime()). + WithSpotifyURL(string(tracks.SimpleTrack.URI)). + WithTrackNumber(tracks.TrackNumber). + Build() + + if err != nil { + return nil, errors.NewUnexpectedError(messages.SongBuildErr, err) + } + + return trackIntance, nil +} + func NewUserSpotifyRepository() *UserSpotifyRepository { return &UserSpotifyRepository{} }