-
Notifications
You must be signed in to change notification settings - Fork 7
Yana P #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Yana P #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package net.hackyourfuture.backend.week6.postify; | ||
|
|
||
| import org.springframework.boot.context.event.ApplicationReadyEvent; | ||
| import org.springframework.context.event.EventListener; | ||
| import org.springframework.jdbc.core.JdbcTemplate; | ||
|
|
||
| public class DBConnectionCheck { | ||
|
|
||
| private final JdbcTemplate jdbcTemplate; | ||
|
|
||
| public DBConnectionCheck(JdbcTemplate jdbcTemplate){ | ||
| this.jdbcTemplate = jdbcTemplate; | ||
| } | ||
|
|
||
| @EventListener(ApplicationReadyEvent.class) | ||
| public void checkConnection(){ | ||
| String result = this.jdbcTemplate.queryForObject("SELECT 'connected'", String.class); | ||
| System.out.println("Database status: " + result); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package net.hackyourfuture.backend.week6.postify.client; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.http.HttpHeaders; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.web.client.RestClient; | ||
|
|
||
| @Configuration | ||
| public class LyricsApiConfiguration { | ||
|
|
||
| @Bean | ||
| public RestClient.Builder restClientBuilder() { | ||
| return RestClient.builder(); | ||
| } | ||
|
|
||
| @Bean | ||
| public RestClient lyricsRestClient(RestClient.Builder builder){ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nicely configured |
||
| return builder.baseUrl("https://api.lyrics.ovh") | ||
| .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) | ||
| .build(); | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice idea to wrap the external REST API calls in a dedicated service. This keeps the integration logic isolated and makes the code easier to maintain and test. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package net.hackyourfuture.backend.week6.postify.client; | ||
|
|
||
| import net.hackyourfuture.backend.week6.postify.dto.response.lyric.LyricApiResponse; | ||
| import org.springframework.beans.factory.annotation.Qualifier; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.web.client.HttpClientErrorException; | ||
| import org.springframework.web.client.RestClient; | ||
|
|
||
| @Service | ||
| public class LyricsClient { | ||
| private final RestClient lyricsRestClient; | ||
|
|
||
| public LyricsClient(@Qualifier("lyricsRestClient") RestClient lyricsRestClient){ | ||
| this.lyricsRestClient = lyricsRestClient; | ||
| } | ||
|
|
||
| public LyricApiResponse getLyrics(String artistName, String trackTitle){ | ||
| try { | ||
| return lyricsRestClient.get() | ||
| .uri(uriBuilder -> uriBuilder | ||
| .path("/v1/{artist}/{title}") | ||
| .build(artistName, trackTitle)) | ||
| .retrieve() | ||
| .body(LyricApiResponse.class); | ||
| } catch(HttpClientErrorException.NotFound ex){ | ||
| return null; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning |
||
| } | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package net.hackyourfuture.backend.week6.postify.controllers; | ||
|
|
||
| import net.hackyourfuture.backend.week6.postify.dto.response.lyric.TrackLyricResponse; | ||
| import net.hackyourfuture.backend.week6.postify.services.TrackLyricsService; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/tracks") | ||
| public class TrackLyricsController { | ||
| private final TrackLyricsService service; | ||
|
|
||
| public TrackLyricsController(TrackLyricsService service){ | ||
| this.service = service; | ||
| } | ||
|
|
||
| @GetMapping("/{id}/lyrics") | ||
| public TrackLyricResponse getLyrics(@PathVariable("id") int trackId){ | ||
| return service.getLyricsForTrack(trackId); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package net.hackyourfuture.backend.week6.postify.controllers; | ||
|
|
||
| import net.hackyourfuture.backend.week6.postify.dto.response.user.UserStatisticsResponse; | ||
| import net.hackyourfuture.backend.week6.postify.services.UserStatisticsService; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/users") | ||
| public class UserStatisticsController { | ||
|
|
||
| private final UserStatisticsService service; | ||
|
|
||
| public UserStatisticsController(UserStatisticsService service){ | ||
| this.service = service; | ||
| } | ||
|
|
||
| @GetMapping("/{id}/stats") | ||
| public UserStatisticsResponse getUserStats(@PathVariable int id){ | ||
| return service.getStatisticsForUser(id); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package net.hackyourfuture.backend.week6.postify.dto.response.lyric; | ||
|
|
||
| public record LyricApiResponse(String lyrics) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package net.hackyourfuture.backend.week6.postify.dto.response.lyric; | ||
|
|
||
| public record TrackBasicInfo (int trackId, String trackTitle, String artistName){ | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package net.hackyourfuture.backend.week6.postify.dto.response.lyric; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record TrackLyricResponse (int trackId, String trackTitle, String artistName, List<String> lyrics){ | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package net.hackyourfuture.backend.week6.postify.dto.response.user; | ||
|
|
||
| public record UserBasicInfoResponse(int id, String name, String country) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package net.hackyourfuture.backend.week6.postify.dto.response.user; | ||
|
|
||
| public record UserStatisticsResponse( | ||
| int userId, | ||
| String userName, | ||
| String userCountry, | ||
| int totalStreams, | ||
| int uniqueTracksStreamed, | ||
| int uniqueArtistsStreamed, | ||
| String favoriteGenre, | ||
| int totalListeningTimeSeconds | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package net.hackyourfuture.backend.week6.postify.exceptions; | ||
|
|
||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
|
||
| @RestControllerAdvice | ||
| public class GlobalExceptionHandler { | ||
|
|
||
| @ExceptionHandler(UserNotFoundException.class) | ||
| public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex){ | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); | ||
| } | ||
|
|
||
| @ExceptionHandler(TrackNotFoundException.class) | ||
| public ResponseEntity<String> handleTrackNotFound(TrackNotFoundException ex){ | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); | ||
| } | ||
|
|
||
| @ExceptionHandler(LyricsNotFoundException.class) | ||
| public ResponseEntity<String> handleLyricsNotFound(LyricsNotFoundException ex){ | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package net.hackyourfuture.backend.week6.postify.exceptions; | ||
|
|
||
| public class LyricsNotFoundException extends RuntimeException{ | ||
| public LyricsNotFoundException(String trackTitle, String artistName){ | ||
| super("Lyrics not found for track '" + trackTitle + "' by '" + artistName + "'"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package net.hackyourfuture.backend.week6.postify.exceptions; | ||
|
|
||
| public class TrackNotFoundException extends RuntimeException{ | ||
| public TrackNotFoundException(int id){ | ||
| super("Track with id " + id + " not found"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package net.hackyourfuture.backend.week6.postify.exceptions; | ||
|
|
||
| public class UserNotFoundException extends RuntimeException{ | ||
| public UserNotFoundException(int id){ | ||
| super("User with id " + id + " not found"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package net.hackyourfuture.backend.week6.postify.repository; | ||
|
|
||
| import net.hackyourfuture.backend.week6.postify.dto.response.lyric.TrackBasicInfo; | ||
| import org.springframework.jdbc.core.JdbcTemplate; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| @Repository | ||
| public class TracksLyricsRepository { | ||
| private final JdbcTemplate jdbcTemplate; | ||
|
|
||
| public TracksLyricsRepository(JdbcTemplate jdbcTemplate){ | ||
| this.jdbcTemplate = jdbcTemplate; | ||
| } | ||
|
|
||
| public Optional<TrackBasicInfo> findTrackWithArtist(int trackId){ | ||
| String sql = """ | ||
| SELECT t.track_id, t.track_title, a.artist_name | ||
| FROM tracks t | ||
| JOIN albums al ON t.album_id = al.album_id | ||
| JOIN artists a ON al.artist_id = a.artist_id | ||
| WHERE t.track_id = ? | ||
| """; | ||
|
|
||
| return jdbcTemplate.query(sql,rs -> { | ||
| if(rs.next()){ | ||
| return Optional.of(new TrackBasicInfo( | ||
| rs.getInt("track_id"), | ||
| rs.getString("track_title"), | ||
| rs.getString("artist_name") | ||
| )); | ||
| } return Optional.empty(); | ||
| }, trackId); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a useful startup check to verify that the application can connect to the database when it becomes ready. Consider using a logger instead of
System.out.println, and make sure this class is registered as a Spring bean, for example with@Component.