diff --git a/postify/pom.xml b/postify/pom.xml
index 7cea5f7..da9cfcb 100644
--- a/postify/pom.xml
+++ b/postify/pom.xml
@@ -35,19 +35,26 @@
spring-boot-starter-webmvc
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
org.postgresql
postgresql
runtime
+
org.projectlombok
lombok
true
+
org.springframework.boot
- spring-boot-starter-webmvc-test
+ spring-boot-starter-test
test
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/config/WebClientConfig.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/config/WebClientConfig.java
new file mode 100644
index 0000000..7fee770
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/config/WebClientConfig.java
@@ -0,0 +1,16 @@
+package net.hackyourfuture.backend.week6.postify.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestClient;
+
+@Configuration
+public class WebClientConfig {
+
+ @Bean
+ public RestClient lyricsRestClient() {
+ return RestClient.builder()
+ .baseUrl("https://api.lyrics.ovh/v1")
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/controller/TrackController.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/controller/TrackController.java
new file mode 100644
index 0000000..2bc5863
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/controller/TrackController.java
@@ -0,0 +1,22 @@
+package net.hackyourfuture.backend.week6.postify.controller;
+
+import net.hackyourfuture.backend.week6.postify.dto.TrackLyricsDto;
+import net.hackyourfuture.backend.week6.postify.service.TrackLyricsService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class TrackController {
+
+ private final TrackLyricsService trackLyricsService;
+
+ public TrackController(TrackLyricsService trackLyricsService) {
+ this.trackLyricsService = trackLyricsService;
+ }
+
+ @GetMapping("/tracks/{id}/lyrics")
+ public TrackLyricsDto getLyrics(@PathVariable("id") Long id) {
+ return trackLyricsService.getTrackLyrics(id);
+ }
+}
\ No newline at end of file
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/controller/UserStatsController.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/controller/UserStatsController.java
new file mode 100644
index 0000000..679f3b5
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/controller/UserStatsController.java
@@ -0,0 +1,20 @@
+package net.hackyourfuture.backend.week6.postify.controller;
+
+import lombok.RequiredArgsConstructor;
+import net.hackyourfuture.backend.week6.postify.dto.UserStatsDto;
+import net.hackyourfuture.backend.week6.postify.service.UserStatsService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequiredArgsConstructor
+public class UserStatsController {
+
+ private final UserStatsService userStatsService;
+
+ @GetMapping("/users/{id}/stats")
+ public UserStatsDto getUserStats(@PathVariable("id") Integer id) {
+ return userStatsService.getUserStats(id);
+ }
+}
\ No newline at end of file
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/ExternalLyricsResponse.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/ExternalLyricsResponse.java
new file mode 100644
index 0000000..936db9a
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/ExternalLyricsResponse.java
@@ -0,0 +1,5 @@
+package net.hackyourfuture.backend.week6.postify.dto;
+
+public record ExternalLyricsResponse(String lyrics) {
+
+}
\ No newline at end of file
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/TrackLyricsDto.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/TrackLyricsDto.java
new file mode 100644
index 0000000..e353023
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/TrackLyricsDto.java
@@ -0,0 +1,25 @@
+package net.hackyourfuture.backend.week6.postify.dto;
+
+public class TrackLyricsDto {
+ private Long trackId;
+ private String trackTitle;
+ private String artistName;
+ private String lyrics;
+
+ public TrackLyricsDto(Long trackId, String trackTitle, String artistName, String lyrics) {
+ this.trackId = trackId;
+ this.trackTitle = trackTitle;
+ this.artistName = artistName;
+ this.lyrics = lyrics;
+ }
+
+ // Getters and Setters
+ public Long getTrackId() { return trackId; }
+ public void setTrackId(Long trackId) { this.trackId = trackId; }
+ public String getTrackTitle() { return trackTitle; }
+ public void setTrackTitle(String trackTitle) { this.trackTitle = trackTitle; }
+ public String getArtistName() { return artistName; }
+ public void setArtistName(String artistName) { this.artistName = artistName; }
+ public String getLyrics() { return lyrics; }
+ public void setLyrics(String lyrics) { this.lyrics = lyrics; }
+}
\ No newline at end of file
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/UserStatsDto.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/UserStatsDto.java
new file mode 100644
index 0000000..f16176e
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/dto/UserStatsDto.java
@@ -0,0 +1,17 @@
+package net.hackyourfuture.backend.week6.postify.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class UserStatsDto {
+ private Integer userId;
+ private String userName;
+ private String userCountry;
+ private Long totalStreams;
+ private Long uniqueTracksStreamed;
+ private Long uniqueArtistsStreamed;
+ private Long totalListeningTimeSeconds;
+ private String favoriteGenre;
+}
\ No newline at end of file
diff --git a/postify/src/main/java/net/hackyourfuture/backend/week6/postify/exception/GlobalExceptionHandler.java b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..6caf60c
--- /dev/null
+++ b/postify/src/main/java/net/hackyourfuture/backend/week6/postify/exception/GlobalExceptionHandler.java
@@ -0,0 +1,20 @@
+package net.hackyourfuture.backend.week6.postify.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.client.HttpClientErrorException;
+import java.util.Map;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ // Catches any 404 errors thrown by RestClient when the external lyrics API comes up empty
+ @ExceptionHandler(HttpClientErrorException.NotFound.class)
+ public ResponseEntity