From b0b026c38e9a6cae4139aababe7db9a35c203d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnau=20Blanch=20Cort=C3=A8s?= Date: Sat, 24 Dec 2022 17:43:44 +0100 Subject: [PATCH] feat: added endpoints to get artist and artist setlists fix: return JSON when error happens --- api/build.gradle | 1 + .../application/ArtistService.java | 6 + .../application/PlaylistService.java | 4 +- .../application/SetlistService.java | 10 +- .../config/BasicErrorController.java | 29 +++ .../CacheConfig.java | 2 +- .../domain/entities/Artist.java | 1 + .../domain/entities/BasicSetlist.java | 13 ++ .../domain/entities/PagedList.java | 13 ++ .../exceptions/ArtistNotFoundException.java | 9 + .../ArtistSetlistsNotFoundException.java | 11 ++ .../domain/ports/ArtistRepository.java | 3 + .../domain/ports/SetlistRepository.java | 4 + .../controller/ArtistController.java | 45 ++++- .../controller/SetlistController.java | 2 +- .../controller/mapper/SetlistMapper.java | 3 + .../controller/response/ArtistResponse.java | 2 + .../response/ArtistSetlistsResponse.java | 20 ++ .../response/BasicSetlistResponse.java | 17 ++ .../controller/response/SetlistResponse.java | 4 +- .../repository/base/ArtistRepositoryBase.java | 10 +- .../base/MusicPlatformRepository.java | 2 +- .../base/SetlistRepositoryBase.java | 15 +- .../repository/setlistfm/SetlistFmApi.java | 8 + .../setlistfm/SetlistFmApiService.java | 38 ++++ .../repository/setlistfm/SetlistFmMapper.java | 14 +- .../model/SetlistFmArtistSetlistsPage.java | 17 ++ .../repository/spotify/SpotifyMapper.java | 3 +- .../repository/spotify/SpotifyRepository.java | 9 +- .../spotify/SpotifySetlistRepository.java | 2 +- .../SeleniumPlaylistImageGenerator.java | 2 +- api/src/main/resources/application.yaml | 7 - ...istResponseToPlaylistApplicationTests.java | 19 ++ .../application/ArtistServiceTest.java | 26 +++ .../application/PlaylistServiceTest.java | 4 +- .../application/SetlistServiceTest.java | 90 ++++++--- .../controller/ArtistControllerTest.java | 55 +++++- .../controller/SetlistControllerTest.java | 4 +- .../setlistfm/SetlistFmApiServiceTest.java | 106 ++++++++-- .../spotify/SpotifyApiServiceTest.java | 7 + ...potifyArtistRepositoryIntegrationTest.java | 21 +- ...tifyPlaylistRepositoryIntegrationTest.java | 3 +- ...otifySetlistRepositoryIntegrationTest.java | 26 ++- .../mappings/accountsSpotify_accessToken.json | 2 +- .../mappings/setlistFm_getArtist.json | 27 +++ .../mappings/setlistFm_getArtistSetlists.json | 186 ++++++++++++++++++ .../mappings/setlistFm_getSetlist.json | 2 +- 47 files changed, 808 insertions(+), 96 deletions(-) create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/config/BasicErrorController.java rename api/src/main/java/xyz/arnau/setlisttoplaylist/{infrastructure => config}/CacheConfig.java (96%) create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/BasicSetlist.java create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/PagedList.java create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistNotFoundException.java create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistSetlistsNotFoundException.java create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistSetlistsResponse.java create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/BasicSetlistResponse.java create mode 100644 api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/model/SetlistFmArtistSetlistsPage.java rename api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/{ => spotify}/SpotifyArtistRepositoryIntegrationTest.java (78%) rename api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/{ => spotify}/SpotifyPlaylistRepositoryIntegrationTest.java (91%) rename api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/{ => spotify}/SpotifySetlistRepositoryIntegrationTest.java (65%) create mode 100644 api/src/test/resources/mappings/setlistFm_getArtist.json create mode 100644 api/src/test/resources/mappings/setlistFm_getArtistSetlists.json diff --git a/api/build.gradle b/api/build.gradle index a3f538c..2bc3046 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -28,6 +28,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-devtools' implementation 'com.github.ben-manes.caffeine:caffeine:3.1.2' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/application/ArtistService.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/application/ArtistService.java index 7faad32..4466ee1 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/application/ArtistService.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/application/ArtistService.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import xyz.arnau.setlisttoplaylist.domain.entities.Artist; +import xyz.arnau.setlisttoplaylist.domain.exceptions.ArtistNotFoundException; import xyz.arnau.setlisttoplaylist.domain.ports.ArtistRepository; import java.util.List; @@ -19,4 +20,9 @@ public List getTopArtists() { public List searchByName(String nameQuery) { return artistRepository.searchByName(nameQuery); } + + public Artist getById(String artistId) { + return artistRepository.getById(artistId) + .orElseThrow(ArtistNotFoundException::new); + } } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/application/PlaylistService.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/application/PlaylistService.java index 64709c8..6deb1f6 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/application/PlaylistService.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/application/PlaylistService.java @@ -30,7 +30,7 @@ public class PlaylistService { "Setlist for ${artistName} concert at ${venueName} (${venueCity}, ${venueCountryCode}) on ${date}."; public Playlist createFromSetlist(String setlistId, boolean isPublic, String authorizationHeader) { - var setlist = setlistService.getSetlist(setlistId); + var setlist = setlistService.getById(setlistId); CreatePlaylistCommand command = CreatePlaylistCommand.builder() .name(fillData(PLAYLIST_NAME, setlist)) @@ -43,7 +43,7 @@ public Playlist createFromSetlist(String setlistId, boolean isPublic, String aut } public byte[] getCoverImage(String setlistId) { - var setlist = setlistService.getSetlist(setlistId); + var setlist = setlistService.getById(setlistId); return playlistImageGenerator.generateImage(setlist); } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/application/SetlistService.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/application/SetlistService.java index a950703..90e13a7 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/application/SetlistService.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/application/SetlistService.java @@ -2,7 +2,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import xyz.arnau.setlisttoplaylist.domain.entities.BasicSetlist; +import xyz.arnau.setlisttoplaylist.domain.entities.PagedList; import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; +import xyz.arnau.setlisttoplaylist.domain.exceptions.ArtistSetlistsNotFoundException; import xyz.arnau.setlisttoplaylist.domain.exceptions.SetlistNotFoundException; import xyz.arnau.setlisttoplaylist.domain.ports.SetlistRepository; @@ -12,9 +15,14 @@ public class SetlistService { private final SetlistRepository setlistRepository; - public Setlist getSetlist(String setlistId) { + public Setlist getById(String setlistId) { return setlistRepository.getSetlist(setlistId) .filter(setlist -> setlist.songs() != null && !setlist.songs().isEmpty()) .orElseThrow(SetlistNotFoundException::new); } + + public PagedList getByArtistId(String artistId, int page) { + return setlistRepository.getArtistSetlists(artistId, page) + .orElseThrow(ArtistSetlistsNotFoundException::new); + } } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/config/BasicErrorController.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/config/BasicErrorController.java new file mode 100644 index 0000000..a276716 --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/config/BasicErrorController.java @@ -0,0 +1,29 @@ +package xyz.arnau.setlisttoplaylist.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController; +import org.springframework.boot.web.servlet.error.ErrorAttributes; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.Map; + +import static org.springframework.boot.web.error.ErrorAttributeOptions.Include.MESSAGE; +import static org.springframework.boot.web.error.ErrorAttributeOptions.of; + +@Controller +@RequestMapping("${server.error.path:${error.path:/error}}") +public class BasicErrorController extends AbstractErrorController { + public BasicErrorController(ErrorAttributes errorAttributes) { + super(errorAttributes); + } + + @RequestMapping + public ResponseEntity> error(HttpServletRequest request) { + Map body = getErrorAttributes(request, of(MESSAGE)); + HttpStatus status = getStatus(request); + return new ResponseEntity<>(body, status); + } +} \ No newline at end of file diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/CacheConfig.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/config/CacheConfig.java similarity index 96% rename from api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/CacheConfig.java rename to api/src/main/java/xyz/arnau/setlisttoplaylist/config/CacheConfig.java index 60b6ea8..cee609b 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/CacheConfig.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/config/CacheConfig.java @@ -1,4 +1,4 @@ -package xyz.arnau.setlisttoplaylist.infrastructure; +package xyz.arnau.setlisttoplaylist.config; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.CacheManager; diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/Artist.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/Artist.java index e6399ba..77e2248 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/Artist.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/Artist.java @@ -4,6 +4,7 @@ @Builder public record Artist( + String id, String musicPlatformId, String name, String imageUrl diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/BasicSetlist.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/BasicSetlist.java new file mode 100644 index 0000000..ebaf4ef --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/BasicSetlist.java @@ -0,0 +1,13 @@ +package xyz.arnau.setlisttoplaylist.domain.entities; + +import lombok.Builder; + +import java.time.LocalDate; + +@Builder +public record BasicSetlist( + String id, + LocalDate date, + Venue venue, + int numSongs +) {} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/PagedList.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/PagedList.java new file mode 100644 index 0000000..38752e8 --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/entities/PagedList.java @@ -0,0 +1,13 @@ +package xyz.arnau.setlisttoplaylist.domain.entities; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record PagedList( + List items, + int page, + int totalItems, + int itemsPerPage +) {} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistNotFoundException.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistNotFoundException.java new file mode 100644 index 0000000..0f5425d --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistNotFoundException.java @@ -0,0 +1,9 @@ +package xyz.arnau.setlisttoplaylist.domain.exceptions; + +import org.springframework.web.bind.annotation.ResponseStatus; + +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@ResponseStatus(value = NOT_FOUND, reason = "Artist not found") +public class ArtistNotFoundException extends RuntimeException { +} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistSetlistsNotFoundException.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistSetlistsNotFoundException.java new file mode 100644 index 0000000..6ad342c --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/exceptions/ArtistSetlistsNotFoundException.java @@ -0,0 +1,11 @@ +package xyz.arnau.setlisttoplaylist.domain.exceptions; + +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@ResponseStatus(value = NOT_FOUND, reason = "Artist setlists not found") +@ResponseBody +public class ArtistSetlistsNotFoundException extends RuntimeException { +} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/ArtistRepository.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/ArtistRepository.java index fccd5cc..bc2dd34 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/ArtistRepository.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/ArtistRepository.java @@ -3,8 +3,11 @@ import xyz.arnau.setlisttoplaylist.domain.entities.Artist; import java.util.List; +import java.util.Optional; public interface ArtistRepository { List getTopArtists(); List searchByName(String nameQuery); + + Optional getById(String artistId); } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/SetlistRepository.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/SetlistRepository.java index 91d7ece..8409f52 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/SetlistRepository.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/domain/ports/SetlistRepository.java @@ -1,9 +1,13 @@ package xyz.arnau.setlisttoplaylist.domain.ports; +import xyz.arnau.setlisttoplaylist.domain.entities.BasicSetlist; +import xyz.arnau.setlisttoplaylist.domain.entities.PagedList; import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; import java.util.Optional; public interface SetlistRepository { Optional getSetlist(String setlistId); + + Optional> getArtistSetlists(String artistId, int page); } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistController.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistController.java index 7e13bd8..b501695 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistController.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistController.java @@ -9,13 +9,13 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import xyz.arnau.setlisttoplaylist.application.ArtistService; +import xyz.arnau.setlisttoplaylist.application.SetlistService; import xyz.arnau.setlisttoplaylist.infrastructure.controller.mapper.ArtistMapper; +import xyz.arnau.setlisttoplaylist.infrastructure.controller.mapper.SetlistMapper; import xyz.arnau.setlisttoplaylist.infrastructure.controller.response.ArtistResponse; +import xyz.arnau.setlisttoplaylist.infrastructure.controller.response.ArtistSetlistsResponse; import xyz.arnau.setlisttoplaylist.infrastructure.controller.response.PlaylistResponse; import java.util.ArrayList; @@ -32,6 +32,7 @@ public class ArtistController { private final ArtistService artistService; + private final SetlistService setlistService; @GetMapping("top") @Operation(summary = "Get top artists") @@ -64,4 +65,38 @@ public ResponseEntity> searchArtistsByName( .map(ArtistMapper.MAPPER::toResponse) .collect(toList())); } -} + + @GetMapping("{artistId}") + @Operation(summary = "Get artist") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Artist found", content = { + @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = ArtistResponse.class)) + }), + @ApiResponse(responseCode = "404", description = "Artist not found", content = @Content) + }) + public ResponseEntity getArtistSetlists( + @PathVariable @Parameter(description = "Artist ID", example = "d15721d8-56b4-453d-b506-fc915b14cba2") String artistId) { + var artist = artistService.getById(artistId); + return ResponseEntity.ok(ArtistMapper.MAPPER.toResponse(artist)); + } + + @GetMapping("{artistId}/setlists") + @Operation(summary = "Get artist setlists") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Artist setlists found", content = { + @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = ArtistSetlistsResponse.class)) + }), + @ApiResponse(responseCode = "404", description = "Artist setlists not found", content = @Content) + }) + public ResponseEntity getArtistSetlists( + @PathVariable @Parameter(description = "Artist ID", example = "d15721d8-56b4-453d-b506-fc915b14cba2") String artistId, + @RequestParam(defaultValue = "1") @Parameter(description = "Page number, starting from 1", example = "1") int page) { + var setlistsPage = setlistService.getByArtistId(artistId, page); + return ResponseEntity.ok(ArtistSetlistsResponse.builder() + .setlists(setlistsPage.items().stream().map(SetlistMapper.MAPPER::toResponse).toList()) + .page(setlistsPage.page()) + .totalItems(setlistsPage.totalItems()) + .itemsPerPage(setlistsPage.itemsPerPage()) + .build()); + } +} \ No newline at end of file diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistController.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistController.java index b9703be..ddedb9b 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistController.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistController.java @@ -39,7 +39,7 @@ public class SetlistController { }) public ResponseEntity getSetlist( @PathVariable @Parameter(description = "Setlist ID", example = "6bb43616") String setlistId) { - Setlist setlist = setlistService.getSetlist(setlistId); + Setlist setlist = setlistService.getById(setlistId); return ok(SetlistMapper.MAPPER.toResponse(setlist)); } } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/mapper/SetlistMapper.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/mapper/SetlistMapper.java index 8f422df..b419871 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/mapper/SetlistMapper.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/mapper/SetlistMapper.java @@ -2,7 +2,9 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; +import xyz.arnau.setlisttoplaylist.domain.entities.BasicSetlist; import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; +import xyz.arnau.setlisttoplaylist.infrastructure.controller.response.BasicSetlistResponse; import xyz.arnau.setlisttoplaylist.infrastructure.controller.response.SetlistResponse; @Mapper @@ -11,4 +13,5 @@ public interface SetlistMapper { SetlistMapper MAPPER = Mappers.getMapper(SetlistMapper.class); SetlistResponse toResponse(Setlist setlist); + BasicSetlistResponse toResponse(BasicSetlist setlist); } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistResponse.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistResponse.java index ae0f729..8cc79c2 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistResponse.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistResponse.java @@ -4,6 +4,8 @@ @Schema(requiredProperties = {"musicPlatformId", "name", "imageUrl"}) public record ArtistResponse( + @Schema(description = "Artist ID", example = "d15721d8-56b4-453d-b506-fc915b14cba2") + String id, @Schema(description = "Music platform artist ID", example = "7mnBLXK823vNxN3UWB7Gfz") String musicPlatformId, @Schema(description = "Artist name", example = "The Black Keys") diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistSetlistsResponse.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistSetlistsResponse.java new file mode 100644 index 0000000..83b29a4 --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/ArtistSetlistsResponse.java @@ -0,0 +1,20 @@ +package xyz.arnau.setlisttoplaylist.infrastructure.controller.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +import java.util.List; + +@Schema(requiredProperties = {"setlists", "page", "totalItems", "itemsPerPage"}) +@Builder +public record ArtistSetlistsResponse( + @Schema(description = "Artist setlists") + List setlists, + @Schema(description = "Page number", example = "1") + int page, + @Schema(description = "Total number of setlists for the artist", example = "56") + int totalItems, + @Schema(description = "Number of items per page", example = "20") + int itemsPerPage +) { +} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/BasicSetlistResponse.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/BasicSetlistResponse.java new file mode 100644 index 0000000..4092581 --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/BasicSetlistResponse.java @@ -0,0 +1,17 @@ +package xyz.arnau.setlisttoplaylist.infrastructure.controller.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.time.LocalDate; + +@Schema(requiredProperties = {"id", "date", "venue", "numSongs"}) +public record BasicSetlistResponse( + @Schema(description = "Setlist ID", example = "6bbf4e1e") + String id, + @Schema(description = "Setlist date") + LocalDate date, + VenueResponse venue, + @Schema(description = "Number of songs", example = "23") + int numSongs +) { +} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/SetlistResponse.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/SetlistResponse.java index f3f4e81..7fd51f2 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/SetlistResponse.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/response/SetlistResponse.java @@ -5,8 +5,10 @@ import java.time.LocalDate; import java.util.List; -@Schema(requiredProperties = {"date", "artist", "venue", "songs"}) +@Schema(requiredProperties = {"id", "date", "artist", "venue", "songs"}) public record SetlistResponse( + @Schema(description = "Setlist ID", example = "6bbf4e1e") + String id, @Schema(description = "Setlist date") LocalDate date, ArtistResponse artist, diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/ArtistRepositoryBase.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/ArtistRepositoryBase.java index 43dd052..39e56ad 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/ArtistRepositoryBase.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/ArtistRepositoryBase.java @@ -4,7 +4,6 @@ import xyz.arnau.setlisttoplaylist.domain.entities.Artist; import xyz.arnau.setlisttoplaylist.domain.ports.ArtistRepository; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.SetlistFmApiService; -import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtist; import java.util.List; import java.util.Optional; @@ -28,13 +27,18 @@ public List getTopArtists() { public List searchByName(String nameQuery) { try { return setlistFmApiService.searchArtists(nameQuery).stream() - .map(SetlistFmArtist::getSortName) .collect( - parallel(musicPlatformRepository::getArtist, toList(), executorService, 10)) + parallel(setlistArtist -> musicPlatformRepository.getArtist(setlistArtist.getId(), setlistArtist.getSortName()), + toList(), executorService, 10)) .get().stream() .filter(Optional::isPresent).map(Optional::get).collect(toList()); } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } } + + public Optional getById(String artistId) { + return setlistFmApiService.getArtist(artistId) + .flatMap(setlistArtist -> musicPlatformRepository.getArtist(setlistArtist.getId(), setlistArtist.getSortName())); + } } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/MusicPlatformRepository.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/MusicPlatformRepository.java index 566e5eb..22e3a2e 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/MusicPlatformRepository.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/MusicPlatformRepository.java @@ -7,7 +7,7 @@ import java.util.Optional; public interface MusicPlatformRepository { - Optional getArtist(String nameQuery); + Optional getArtist(String id, String name); Optional getSong(String artistName, String songName, boolean isCover); List getTopArtists(); } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/SetlistRepositoryBase.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/SetlistRepositoryBase.java index cddf12f..4c7846b 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/SetlistRepositoryBase.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/base/SetlistRepositoryBase.java @@ -1,10 +1,13 @@ package xyz.arnau.setlisttoplaylist.infrastructure.repository.base; import lombok.RequiredArgsConstructor; +import xyz.arnau.setlisttoplaylist.domain.entities.BasicSetlist; +import xyz.arnau.setlisttoplaylist.domain.entities.PagedList; import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; import xyz.arnau.setlisttoplaylist.domain.entities.Song; import xyz.arnau.setlisttoplaylist.domain.ports.SetlistRepository; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.SetlistFmApiService; +import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.SetlistFmMapper; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtist; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmSet; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmSong; @@ -27,13 +30,23 @@ public abstract class SetlistRepositoryBase getSetlist(String setlistId) { return setlistFmApiService.getSetlist(setlistId) - .flatMap(setlist -> musicPlatformRepository.getArtist(setlist.getArtist().getName()) + .flatMap(setlist -> musicPlatformRepository.getArtist(setlist.getArtist().getId(), setlist.getArtist().getName()) .map(artist -> { var songs = mapSongs(setlist.getSets().getSet(), setlist.getArtist()); return mapSetlist(setlist, artist, songs); })); } + public Optional> getArtistSetlists(String artistId, int page) { + return setlistFmApiService.getArtistSetlists(artistId, page) + .map(setlistsPage -> PagedList.builder() + .items(setlistsPage.getSetlists().stream().map(SetlistFmMapper::mapSetlist).collect(toList())) + .page(setlistsPage.getPage()) + .itemsPerPage(setlistsPage.getItemsPerPage()) + .totalItems(setlistsPage.getTotal()) + .build()); + } + private List mapSongs(List sets, SetlistFmArtist artist) { try { return sets.stream() diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApi.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApi.java index 964e58e..39facbb 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApi.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApi.java @@ -4,7 +4,9 @@ import retrofit2.http.GET; import retrofit2.http.Path; import retrofit2.http.Query; +import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtist; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtistSearchResult; +import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtistSetlistsPage; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmSetlist; public interface SetlistFmApi { @@ -14,4 +16,10 @@ public interface SetlistFmApi { @GET("/rest/1.0/search/artists") Call searchArtists(@Query("artistName") String name, @Query("sort") String sort); + + @GET("/rest/1.0/artist/{artistId}") + Call getArtist(@Path("artistId") String artistId); + + @GET("/rest/1.0/artist/{artistId}/setlists") + Call getArtistSetlists(@Path("artistId") String artistId, @Query("p") int page); } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiService.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiService.java index 17846c2..0ee47f9 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiService.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiService.java @@ -2,8 +2,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.apachecommons.CommonsLog; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtist; +import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmArtistSetlistsPage; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmSetlist; import java.io.IOException; @@ -13,6 +15,7 @@ import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; import static org.springframework.http.HttpStatus.NOT_FOUND; +import static xyz.arnau.setlisttoplaylist.config.CacheConfig.ARTISTS; @Service @RequiredArgsConstructor @@ -57,4 +60,39 @@ public List searchArtists(String nameQuery) { throw new RuntimeException(e); } } + + @Cacheable(value = ARTISTS, key = "{'setlistFm', #artistId}") + public Optional getArtist(String artistId) { + try { + var response = setlistFmApi.getArtist(artistId).execute(); + if (response.isSuccessful() && response.body() != null) { + return Optional.of(response.body()); + } else if (response.code() == NOT_FOUND.value()) { + return Optional.empty(); + } else { + log.error("Could not get artist (artistId=%s)".formatted(artistId)); + throw new RuntimeException("Setlist.fm API error"); + } + } catch (IOException e) { + log.error("Could not get artist (artistId=%s)".formatted(artistId)); + throw new RuntimeException(e); + } + } + + public Optional getArtistSetlists(String artistId, int page) { + try { + var response = setlistFmApi.getArtistSetlists(artistId, page).execute(); + if (response.isSuccessful() && response.body() != null) { + return Optional.of(response.body()); + } else if (response.code() == NOT_FOUND.value()) { + return Optional.empty(); + } else { + log.error("Could not get artist setlists (artistId=%s, page=%s)".formatted(artistId, page)); + throw new RuntimeException("Setlist.fm API error"); + } + } catch (IOException e) { + log.error("Could not get artist setlists (artistId=%s, page=%s)".formatted(artistId, page)); + throw new RuntimeException(e); + } + } } diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmMapper.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmMapper.java index aa38c60..05cfb64 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmMapper.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmMapper.java @@ -1,9 +1,6 @@ package xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm; -import xyz.arnau.setlisttoplaylist.domain.entities.Artist; -import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; -import xyz.arnau.setlisttoplaylist.domain.entities.Song; -import xyz.arnau.setlisttoplaylist.domain.entities.Venue; +import xyz.arnau.setlisttoplaylist.domain.entities.*; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmSetlist; import xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model.SetlistFmVenue; @@ -24,6 +21,15 @@ public static Setlist mapSetlist(SetlistFmSetlist setlist, Artist artist, List s.getSong().size()).reduce(0, Integer::sum)) + .build(); + } + public static Venue mapVenue(SetlistFmVenue venueResponse) { return Venue.builder() .name(venueResponse.getName()) diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/model/SetlistFmArtistSetlistsPage.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/model/SetlistFmArtistSetlistsPage.java new file mode 100644 index 0000000..b98e171 --- /dev/null +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/model/SetlistFmArtistSetlistsPage.java @@ -0,0 +1,17 @@ +package xyz.arnau.setlisttoplaylist.infrastructure.repository.setlistfm.model; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Value; + +import java.util.List; + +@Builder +@Value +public class SetlistFmArtistSetlistsPage { + @SerializedName("setlist") + List setlists; + int itemsPerPage; + int page; + int total; +} diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyMapper.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyMapper.java index ec4808c..41dbb34 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyMapper.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyMapper.java @@ -7,8 +7,9 @@ import xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify.model.SpotifyTrack; public class SpotifyMapper { - public static Artist mapArtist(SpotifyArtist spotifyArtist) { + public static Artist mapArtist(String artistId, SpotifyArtist spotifyArtist) { return Artist.builder() + .id(artistId) .musicPlatformId(spotifyArtist.getId()) .name(spotifyArtist.getName()) .imageUrl(spotifyArtist.getImages().stream().findFirst().map(SpotifyImage::getUrl).orElse(null)) diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyRepository.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyRepository.java index d929025..8a26aa8 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyRepository.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyRepository.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.Optional; -import static xyz.arnau.setlisttoplaylist.infrastructure.CacheConfig.*; +import static xyz.arnau.setlisttoplaylist.config.CacheConfig.*; @Component @RequiredArgsConstructor @@ -25,9 +25,10 @@ public class SpotifyRepository implements MusicPlatformRepository { @Value("${spotify.topTracksPlaylistId}") private String topTracksPlaylistId; - @Cacheable(value = ARTISTS, key = "{'spotify', #name}") - public Optional getArtist(String name) { - return apiService.searchArtist(name).map(SpotifyMapper::mapArtist); + @Cacheable(value = ARTISTS, key = "{'spotify', #id}") + public Optional getArtist(String id, String name) { + return apiService.searchArtist(name) + .map(spotifyArtist -> SpotifyMapper.mapArtist(id, spotifyArtist)); } @Cacheable(value = SONGS, key = "{'spotify', #artistName, #songName}") diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepository.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepository.java index 348b1ea..93befb1 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepository.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepository.java @@ -8,7 +8,7 @@ import java.util.Optional; -import static xyz.arnau.setlisttoplaylist.infrastructure.CacheConfig.SETLISTS; +import static xyz.arnau.setlisttoplaylist.config.CacheConfig.SETLISTS; @Component public class SpotifySetlistRepository extends SetlistRepositoryBase { diff --git a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/selenium/SeleniumPlaylistImageGenerator.java b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/selenium/SeleniumPlaylistImageGenerator.java index ef0056c..9d359f5 100644 --- a/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/selenium/SeleniumPlaylistImageGenerator.java +++ b/api/src/main/java/xyz/arnau/setlisttoplaylist/infrastructure/selenium/SeleniumPlaylistImageGenerator.java @@ -16,7 +16,7 @@ import static java.time.format.DateTimeFormatter.ofLocalizedDate; import static java.time.format.FormatStyle.LONG; import static java.util.Locale.US; -import static xyz.arnau.setlisttoplaylist.infrastructure.CacheConfig.COVER_IMAGE; +import static xyz.arnau.setlisttoplaylist.config.CacheConfig.COVER_IMAGE; import static xyz.arnau.setlisttoplaylist.infrastructure.selenium.JpegImageConverter.convertImageToJpg; @Component diff --git a/api/src/main/resources/application.yaml b/api/src/main/resources/application.yaml index aca7aa5..a4714ca 100644 --- a/api/src/main/resources/application.yaml +++ b/api/src/main/resources/application.yaml @@ -1,11 +1,4 @@ springdoc.api-docs.path: /api-docs -server.error: - include-message: always - whitelabel: - enabled: false -spring: - autoconfigure: - exclude: 'org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration' setlistFm: api: diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/SetlistResponseToPlaylistApplicationTests.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/SetlistResponseToPlaylistApplicationTests.java index a775e74..2b60224 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/SetlistResponseToPlaylistApplicationTests.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/SetlistResponseToPlaylistApplicationTests.java @@ -47,6 +47,7 @@ public void setUp() { @Test void givenASetlistId_shouldReturnSetlist() { given(). + queryParam("page", "1"). when(). get("/setlists/abc12345"). then(). @@ -103,6 +104,24 @@ void givenAnArtistName_shouldReturnArtists() { } + @Test + void givenAnArtistId_shouldReturnArtistInfo() { + given(). + when(). + get("/artists/4e7209ee-ef02-4cb7-bdff-815b0473c27c"). + then(). + statusCode(OK.value()); + } + + @Test + void givenAnArtistId_shouldReturnArtistSetlists() { + given(). + when(). + get("/artists/4e7209ee-ef02-4cb7-bdff-815b0473c27c/setlists"). + then(). + statusCode(OK.value()); + } + public static class Initializer implements ApplicationContextInitializer { @Override diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/application/ArtistServiceTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/application/ArtistServiceTest.java index 07bf468..f9c68dd 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/application/ArtistServiceTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/application/ArtistServiceTest.java @@ -7,10 +7,14 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import xyz.arnau.setlisttoplaylist.domain.entities.Artist; +import xyz.arnau.setlisttoplaylist.domain.exceptions.ArtistNotFoundException; import xyz.arnau.setlisttoplaylist.domain.ports.ArtistRepository; +import java.util.Optional; + import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -53,4 +57,26 @@ public void givenAnArtistName_shouldReturnArtists() { assertThat(artistsFound).isEqualTo(artists); } } + + @Nested + class GetById { + private static final String ARTIST_ID = "d15721d8-56b4-453d-b506-fc915b14cba2"; + + @Test + public void whenArtistIsFound_shouldReturnArtist() { + var artist = Artist.builder().id(ARTIST_ID).name("Manel").imageUrl("url1").build(); + when(artistRepository.getById(ARTIST_ID)).thenReturn(Optional.of(artist)); + + var artistFound = artistService.getById(ARTIST_ID); + + assertThat(artistFound).isEqualTo(artist); + } + + @Test + public void whenArtistIsNotFound_shouldThrowArtistNotFoundException() { + when(artistRepository.getById("not-found")).thenReturn(Optional.empty()); + + assertThrows(ArtistNotFoundException.class, () -> artistService.getById("not-found")); + } + } } \ No newline at end of file diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/application/PlaylistServiceTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/application/PlaylistServiceTest.java index a81c36c..cf1c58d 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/application/PlaylistServiceTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/application/PlaylistServiceTest.java @@ -46,7 +46,7 @@ public void whenSetlistIsFound_shouldCreatePlaylist() { var setlist = aSetlist(); var imageBytes = "image".getBytes(); - when(setlistService.getSetlist(SETLIST_ID)).thenReturn(setlist); + when(setlistService.getById(SETLIST_ID)).thenReturn(setlist); when(playlistRepository.create(createCommandCaptor.capture(), eq(USER_TOKEN))).thenReturn(playlist); when(playlistImageGenerator.generateImage(setlist)).thenReturn(imageBytes); @@ -66,7 +66,7 @@ public void whenSetlistIsFound_shouldCreatePlaylist() { @Test public void whenCantCreatePlaylistBecauseOfAuthError_shouldThrowException() { var setlist = aSetlist(); - when(setlistService.getSetlist(SETLIST_ID)).thenReturn(setlist); + when(setlistService.getById(SETLIST_ID)).thenReturn(setlist); when(playlistService.createFromSetlist(SETLIST_ID, false, USER_TOKEN)).thenThrow(MusicPlatformAuthException.class); assertThrows(MusicPlatformAuthException.class, () -> playlistService.createFromSetlist(SETLIST_ID, false, USER_TOKEN)); diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/application/SetlistServiceTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/application/SetlistServiceTest.java index 29bea3c..ec17db5 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/application/SetlistServiceTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/application/SetlistServiceTest.java @@ -1,13 +1,13 @@ package xyz.arnau.setlisttoplaylist.application; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import xyz.arnau.setlisttoplaylist.domain.entities.Artist; -import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; -import xyz.arnau.setlisttoplaylist.domain.entities.Song; +import xyz.arnau.setlisttoplaylist.domain.entities.*; +import xyz.arnau.setlisttoplaylist.domain.exceptions.ArtistSetlistsNotFoundException; import xyz.arnau.setlisttoplaylist.domain.exceptions.SetlistNotFoundException; import xyz.arnau.setlisttoplaylist.domain.ports.SetlistRepository; @@ -28,39 +28,71 @@ class SetlistServiceTest { @InjectMocks private SetlistService setlistService; - @Test - public void whenSetlistIsFound_ReturnsSetlist() { - when(setlistRepository.getSetlist("abc12345")) - .thenReturn(Optional.of( - Setlist.builder() - .artist(Artist.builder().name("The Strokes").build()) - .songs(singletonList(Song.builder().name("Last Nite").build())) - .build())); + @Nested + class GetById { + @Test + public void whenSetlistIsFound_ReturnsSetlist() { + when(setlistRepository.getSetlist("abc12345")) + .thenReturn(Optional.of( + Setlist.builder() + .artist(Artist.builder().name("The Strokes").build()) + .songs(singletonList(Song.builder().name("Last Nite").build())) + .build())); - Setlist setlist = setlistService.getSetlist("abc12345"); + Setlist setlist = setlistService.getById("abc12345"); - assertThat(setlist).isNotNull(); - assertThat(setlist.artist()).isNotNull(); - assertThat(setlist.artist().name()).isEqualTo("The Strokes"); - } + assertThat(setlist).isNotNull(); + assertThat(setlist.artist()).isNotNull(); + assertThat(setlist.artist().name()).isEqualTo("The Strokes"); + } + + @Test + public void whenSetlistIsNotFound_ThrowsSetlistNotFoundException() { + when(setlistRepository.getSetlist("abc12345")).thenReturn(Optional.empty()); + + assertThrows(SetlistNotFoundException.class, () -> setlistService.getById("abc12345")); + } - @Test - public void whenSetlistIsNotFound_ThrowsSetlistNotFoundException() { - when(setlistRepository.getSetlist("abc12345")).thenReturn(Optional.empty()); + @Test + public void whenSetlistHasNoSongs_ThrowsSetlistNotFoundException() { + when(setlistRepository.getSetlist("abc12345")) + .thenReturn(Optional.of( + Setlist.builder() + .artist(Artist.builder().name("The Strokes").build()) + .songs(emptyList()) + .build())); + + assertThrows(SetlistNotFoundException.class, () -> setlistService.getById("abc12345")); + } - assertThrows(SetlistNotFoundException.class, () -> setlistService.getSetlist("abc12345")); } + @Nested + class GetByArtistId { + private static final String ARTIST_ID = "4e7209ee-ef02-4cb7-bdff-815b0473c27c"; + + @Test + public void whenSetlistsAreFound_ReturnsEmptyList() { + PagedList setlistList = PagedList.builder() + .items(singletonList(BasicSetlist.builder().id("setlist1").numSongs(15).build())) + .page(1) + .totalItems(1) + .itemsPerPage(20) + .build(); - @Test - public void whenSetlistHasNoSongs_ThrowsSetlistNotFoundException() { - when(setlistRepository.getSetlist("abc12345")) - .thenReturn(Optional.of( - Setlist.builder() - .artist(Artist.builder().name("The Strokes").build()) - .songs(emptyList()) - .build())); + when(setlistRepository.getArtistSetlists(ARTIST_ID, 1)).thenReturn(Optional.of(setlistList)); - assertThrows(SetlistNotFoundException.class, () -> setlistService.getSetlist("abc12345")); + var result = setlistService.getByArtistId(ARTIST_ID, 1); + + assertThat(result).isEqualTo(setlistList); + } + + @Test + public void whenNoSetlistsAreFound_throwsArtistSetlistsNotFoundException() { + when(setlistRepository.getArtistSetlists(ARTIST_ID, 1)).thenReturn(Optional.empty()); + + assertThrows(ArtistSetlistsNotFoundException.class, () -> setlistService.getByArtistId(ARTIST_ID, 1)); + } } + } \ No newline at end of file diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistControllerTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistControllerTest.java index 1bb5621..d07bc6e 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistControllerTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/ArtistControllerTest.java @@ -8,19 +8,26 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import xyz.arnau.setlisttoplaylist.application.ArtistService; +import xyz.arnau.setlisttoplaylist.application.SetlistService; import xyz.arnau.setlisttoplaylist.domain.entities.Artist; +import xyz.arnau.setlisttoplaylist.domain.entities.BasicSetlist; +import xyz.arnau.setlisttoplaylist.domain.entities.PagedList; import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; import static io.restassured.module.mockmvc.RestAssuredMockMvc.standaloneSetup; import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class ArtistControllerTest { + public static final String ARTIST_ID = "4e7209ee-ef02-4cb7-bdff-815b0473c27c"; @Mock private ArtistService artistService; + @Mock + private SetlistService setlistService; @InjectMocks private ArtistController artistController; @@ -30,9 +37,9 @@ public class ArtistControllerTest { void setUp() { standaloneSetup(artistController); } - @Nested class GetTopArtists { + @Test public void shouldReturnOk() { when(artistService.getTopArtists()).thenReturn(asList( @@ -50,11 +57,11 @@ public void shouldReturnOk() { body("$", hasSize(2)); } } - @Nested class GetArtists { + @Test - public void whenArtistsFound_shouldOkWithArtists() { + public void whenArtistsFound_shouldReturnOkWithArtists() { when(artistService.searchByName("Manel")).thenReturn(asList( Artist.builder().name("Manel").imageUrl("img1").build(), Artist.builder().name("Fake Manel").imageUrl("img2").build() @@ -69,5 +76,47 @@ public void whenArtistsFound_shouldOkWithArtists() { body("$", hasSize(2)); } + + } + @Nested + class GetArtist { + @Test + public void whenArtistFound_shouldReturnOkWithArtistInfo() { + when(artistService.getById(ARTIST_ID)).thenReturn(Artist.builder().name("Manel").build()); + given(). + when(). + get("/artists/" + ARTIST_ID). + then(). + statusCode(200) + .body("name", equalTo("Manel")); + } + } + + @Nested + class GetArtistSetlists { + @Test + public void whenArtistFound_shouldReturnOkWithSetlists() { + when(setlistService.getByArtistId(ARTIST_ID, 1)) + .thenReturn(PagedList.builder() + .items(asList( + BasicSetlist.builder().id("53be1b39").build(), + BasicSetlist.builder().id("4bbe1b46").build(), + BasicSetlist.builder().id("73b07e29").build())) + .itemsPerPage(20) + .page(1) + .totalItems(1) + .build()); + + given(). + queryParam("page", 1). + when(). + get("/artists/" + ARTIST_ID + "/setlists"). + then(). + statusCode(200) + .body("setlists.id", equalTo(asList("53be1b39", "4bbe1b46", "73b07e29"))) + .body("page", equalTo(1)) + .body("totalItems", equalTo(1)) + .body("itemsPerPage", equalTo(20)); + } } } diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistControllerTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistControllerTest.java index 9903f57..f0b3c34 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistControllerTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/controller/SetlistControllerTest.java @@ -37,7 +37,7 @@ class GetSetlist { public void whenSetlistIsFound_shouldReturnOk() { var setlist = Setlist.builder().artist(Artist.builder().name("The Strokes").build()).build(); - when(setlistService.getSetlist("abc12345")).thenReturn(setlist); + when(setlistService.getById("abc12345")).thenReturn(setlist); when(). get("/setlists/abc12345"). @@ -48,7 +48,7 @@ public void whenSetlistIsFound_shouldReturnOk() { @Test public void whenSetlistIsNotFound_shouldReturnNotFound() { - when(setlistService.getSetlist("not-found")).thenThrow(SetlistNotFoundException.class); + when(setlistService.getById("not-found")).thenThrow(SetlistNotFoundException.class); when(). get("/setlists/not-found"). diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiServiceTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiServiceTest.java index c353ab8..564957d 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiServiceTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/setlistfm/SetlistFmApiServiceTest.java @@ -4,6 +4,7 @@ import com.google.gson.GsonBuilder; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import retrofit2.Retrofit; @@ -15,6 +16,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.singletonList; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.http.HttpStatus.NOT_FOUND; @@ -36,24 +38,18 @@ public class SetlistFmApiServiceTest { @Nested class GetSetlist { @Test - public void whenSetlistIsFound_returnsSetlist() { - SetlistFmSetlist setlistResponse = SetlistFmSetlist.builder() - .id("12345") - .eventDate("2022-01-01") - .artist(SetlistFmArtist.builder().name("Manel").build()) - .venue(new SetlistFmVenue("Apolo", - new SetlistFmCity("Barcelona", null, "Catalonia", - new SetlistFmCountry("ES", "Spain")))) - .sets(new SetlistFmSets(singletonList(new SetlistFmSet(asList( - SetlistFmSong.builder().name("Benvolgut").build(), - SetlistFmSong.builder().name("Per la bona gent").build() - ))))).build(); + public void whenSetlistIsFound_returnsSetlist() throws InterruptedException { + var setlistResponse = aSetlist(); enqueueOkResponse(setlistResponse); Optional setlist = apiService.getSetlist("12345"); assertThat(setlist).isPresent(); assertThat(setlist.get()).isEqualTo(setlistResponse); + + RecordedRequest request = mockWebServer.takeRequest(); + assertThat(request.getMethod()).isEqualTo("GET"); + assertThat(requireNonNull(request.getRequestUrl()).encodedPath()).isEqualTo("/rest/1.0/setlist/12345"); } @Test @@ -68,8 +64,9 @@ public void whenSetlistIsNotFound_returnsEmpty() { @Nested class SearchArtists { + @Test - public void whenArtistsAreFound_returnsArtists() { + public void whenArtistsAreFound_returnsArtists() throws InterruptedException { enqueueOkResponse(new SetlistFmArtistSearchResult(asList( SetlistFmArtist.builder().name("Arctic Monkeys").build(), SetlistFmArtist.builder().name("Antarctic Monkeys").build() @@ -80,8 +77,13 @@ public void whenArtistsAreFound_returnsArtists() { assertThat(artists).hasSize(2); assertThat(artists.stream().map(SetlistFmArtist::getName).collect(toList())) .containsExactly("Arctic Monkeys", "Antarctic Monkeys"); - } + RecordedRequest request = mockWebServer.takeRequest(); + assertThat(request.getMethod()).isEqualTo("GET"); + assertThat(requireNonNull(request.getRequestUrl()).encodedPath()).isEqualTo("/rest/1.0/search/artists"); + assertThat(request.getRequestUrl().queryParameter("artistName")).isEqualTo("Monkeys"); + assertThat(request.getRequestUrl().queryParameter("sort")).isEqualTo("relevance"); + } @Test public void whenNoArtistFound_returnsEmpty() { mockWebServer.enqueue(new MockResponse().setResponseCode(NOT_FOUND.value())); @@ -91,6 +93,82 @@ public void whenNoArtistFound_returnsEmpty() { assertThat(artists).isEmpty(); } + + } + @Nested + class GetArtist { + + @Test + public void whenArtistIsFound_returnsArtist() throws InterruptedException { + SetlistFmArtist artistResponse = SetlistFmArtist.builder() + .id("4e7209ee-ef02-4cb7-bdff-815b0473c27c") + .name("Manel") + .disambiguation("Manel") + .sortName("Manel") + .build(); + enqueueOkResponse(artistResponse); + + Optional artist = apiService.getArtist("4e7209ee-ef02-4cb7-bdff-815b0473c27c"); + + assertThat(artist).isPresent(); + assertThat(artist.get()).isEqualTo(artistResponse); + + RecordedRequest request = mockWebServer.takeRequest(); + assertThat(request.getMethod()).isEqualTo("GET"); + assertThat(requireNonNull(request.getRequestUrl()).encodedPath()).isEqualTo("/rest/1.0/artist/4e7209ee-ef02-4cb7-bdff-815b0473c27c"); + } + @Test + public void whenArtistIsNotFound_returnsEmpty() { + mockWebServer.enqueue(new MockResponse().setResponseCode(NOT_FOUND.value())); + + Optional artist = apiService.getArtist("4e7209ee-ef02-4cb7-bdff-815b0473c27c"); + + assertThat(artist).isEmpty(); + } + + } + @Nested + class GetArtistSetlists { + + @Test + public void whenArtistSetlistsAreFound_returnsArtist() throws InterruptedException { + var response = SetlistFmArtistSetlistsPage.builder() + .setlists(singletonList(aSetlist())) + .page(1).itemsPerPage(20).total(10).build(); + enqueueOkResponse(response); + + var setlistsPage = apiService.getArtistSetlists("4e7209ee-ef02-4cb7-bdff-815b0473c27c", 1); + + assertThat(setlistsPage).isPresent(); + assertThat(setlistsPage.get()).isEqualTo(response); + + RecordedRequest request = mockWebServer.takeRequest(); + assertThat(request.getMethod()).isEqualTo("GET"); + assertThat(requireNonNull(request.getRequestUrl()).encodedPath()).isEqualTo("/rest/1.0/artist/4e7209ee-ef02-4cb7-bdff-815b0473c27c/setlists"); + assertThat(requireNonNull(request.getRequestUrl()).queryParameter("p")).isEqualTo("1"); + } + @Test + public void whenNoArtistSetlistsAreFound_returnsEmpty() { + mockWebServer.enqueue(new MockResponse().setResponseCode(NOT_FOUND.value())); + + var setlistsPage = apiService.getArtistSetlists("4e7209ee-ef02-4cb7-bdff-815b0473c27c", 1); + + assertThat(setlistsPage).isEmpty(); + } + + } + private static SetlistFmSetlist aSetlist() { + return SetlistFmSetlist.builder() + .id("12345") + .eventDate("2022-01-01") + .artist(SetlistFmArtist.builder().name("Manel").build()) + .venue(new SetlistFmVenue("Apolo", + new SetlistFmCity("Barcelona", null, "Catalonia", + new SetlistFmCountry("ES", "Spain")))) + .sets(new SetlistFmSets(singletonList(new SetlistFmSet(asList( + SetlistFmSong.builder().name("Benvolgut").build(), + SetlistFmSong.builder().name("Per la bona gent").build() + ))))).build(); } private void enqueueOkResponse(Object body) { diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyApiServiceTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyApiServiceTest.java index 4391c79..e420a28 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyApiServiceTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyApiServiceTest.java @@ -106,6 +106,7 @@ public void whenNot200_ThrowsException() { } } + @Nested class SearchArtist { @@ -151,6 +152,7 @@ public void whenNot200_ThrowsException() { } } + @Nested class GetUserId { @@ -177,6 +179,7 @@ public void when401_throwsMusicPlatformAuthException() { } } + @Nested class CreatePlaylist { @@ -212,6 +215,7 @@ public void when401_throwsMusicPlatformAuthException() { } } + @Nested class AddSongsToPlaylist { @@ -240,6 +244,7 @@ public void when401_throwsMusicPlatformAuthException() { } } + @Nested class GetPlaylist { @@ -277,6 +282,7 @@ public void when401_throwsMusicPlatformAuthException() { } } + @Nested class GetSeveralArtists { @@ -308,6 +314,7 @@ public void when401_throwsMusicPlatformAuthException() { } } + private void enqueueOkResponse(Object body) { mockWebServer.enqueue(new MockResponse() .setResponseCode(OK.value()) diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifyArtistRepositoryIntegrationTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyArtistRepositoryIntegrationTest.java similarity index 78% rename from api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifyArtistRepositoryIntegrationTest.java rename to api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyArtistRepositoryIntegrationTest.java index 0103af6..0971232 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifyArtistRepositoryIntegrationTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyArtistRepositoryIntegrationTest.java @@ -1,4 +1,4 @@ -package xyz.arnau.setlisttoplaylist.infrastructure.repository; +package xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import org.junit.jupiter.api.Nested; @@ -7,9 +7,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import xyz.arnau.setlisttoplaylist.domain.entities.Artist; -import xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify.SpotifyArtistRepository; import java.util.List; +import java.util.Optional; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static java.util.Arrays.asList; @@ -51,6 +51,7 @@ public void whenResultsFound_shouldReturnArtists() { assertThat(artists).isEqualTo(singletonList( Artist.builder() + .id("4e7209ee-ef02-4cb7-bdff-815b0473c27c") .musicPlatformId("40tHhop0T30DwienQBmTxb") .name("Manel") .imageUrl("https://i.scdn.co/image/ab6761610000e5ebf03cdcbdda43b390cf876a6a") @@ -65,4 +66,20 @@ public void whenResultsNotFound_shouldReturnEmpty() { assertThat(artists).isEmpty(); } } + + @Nested + class GetById { + @Test + public void shouldReturnArtist() { + Optional artist = artistRepository.getById("4e7209ee-ef02-4cb7-bdff-815b0473c27c"); + + assertThat(artist).isPresent(); + assertThat(artist.get()).isEqualTo(Artist.builder() + .id("4e7209ee-ef02-4cb7-bdff-815b0473c27c") + .musicPlatformId("40tHhop0T30DwienQBmTxb") + .name("Manel") + .imageUrl("https://i.scdn.co/image/ab6761610000e5ebf03cdcbdda43b390cf876a6a") + .build()); + } + } } \ No newline at end of file diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifyPlaylistRepositoryIntegrationTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyPlaylistRepositoryIntegrationTest.java similarity index 91% rename from api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifyPlaylistRepositoryIntegrationTest.java rename to api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyPlaylistRepositoryIntegrationTest.java index 7b5fccd..7b59fc4 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifyPlaylistRepositoryIntegrationTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifyPlaylistRepositoryIntegrationTest.java @@ -1,4 +1,4 @@ -package xyz.arnau.setlisttoplaylist.infrastructure.repository; +package xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import org.junit.jupiter.api.Test; @@ -7,7 +7,6 @@ import org.springframework.boot.test.context.SpringBootTest; import xyz.arnau.setlisttoplaylist.domain.dto.CreatePlaylistCommand; import xyz.arnau.setlisttoplaylist.domain.entities.Playlist; -import xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify.SpotifyPlaylistRepository; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static java.util.Arrays.asList; diff --git a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifySetlistRepositoryIntegrationTest.java b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepositoryIntegrationTest.java similarity index 65% rename from api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifySetlistRepositoryIntegrationTest.java rename to api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepositoryIntegrationTest.java index 6aa91a1..04384c4 100644 --- a/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/SpotifySetlistRepositoryIntegrationTest.java +++ b/api/src/test/java/xyz/arnau/setlisttoplaylist/infrastructure/repository/spotify/SpotifySetlistRepositoryIntegrationTest.java @@ -1,4 +1,4 @@ -package xyz.arnau.setlisttoplaylist.infrastructure.repository; +package xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import org.junit.jupiter.api.Nested; @@ -6,11 +6,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import xyz.arnau.setlisttoplaylist.domain.entities.Artist; -import xyz.arnau.setlisttoplaylist.domain.entities.Setlist; -import xyz.arnau.setlisttoplaylist.domain.entities.Song; -import xyz.arnau.setlisttoplaylist.domain.entities.Venue; -import xyz.arnau.setlisttoplaylist.infrastructure.repository.spotify.SpotifySetlistRepository; +import xyz.arnau.setlisttoplaylist.domain.entities.*; import java.util.Optional; @@ -39,6 +35,7 @@ public void shouldReturnSetlist() { assertThat(setlist).isNotEmpty(); assertThat(setlist.get().date()).isEqualTo("2022-09-18"); assertThat(setlist.get().artist()).isEqualTo(Artist.builder() + .id("4e7209ee-ef02-4cb7-bdff-815b0473c27c") .musicPlatformId("40tHhop0T30DwienQBmTxb") .name("Manel") .imageUrl("https://i.scdn.co/image/ab6761610000e5ebf03cdcbdda43b390cf876a6a") @@ -50,4 +47,21 @@ public void shouldReturnSetlist() { "4KQPAGQNStZaWiewr83fwM", "6ADbZPiWZNsaCiIvsg5iq6", "6lSJZiZqWU8Qt1fJVeFZEv")); } } + + @Nested + class GetArtistSetlists { + @Test + public void shouldReturnArtistSetlists() { + var setlistsPage = setlistRepository.getArtistSetlists("4e7209ee-ef02-4cb7-bdff-815b0473c27c", 1); + + assertThat(setlistsPage).isNotEmpty(); + assertThat(setlistsPage.get().items().stream().map(BasicSetlist::id).collect(toList())) + .isEqualTo(asList("53be1b39", "4bbe1b46", "73b07e29")); + assertThat(setlistsPage.get().items().stream().map(BasicSetlist::numSongs).collect(toList())) + .isEqualTo(asList(0, 0, 6)); + assertThat(setlistsPage.get().page()).isEqualTo(1); + assertThat(setlistsPage.get().totalItems()).isEqualTo(168); + assertThat(setlistsPage.get().itemsPerPage()).isEqualTo(20); + } + } } \ No newline at end of file diff --git a/api/src/test/resources/mappings/accountsSpotify_accessToken.json b/api/src/test/resources/mappings/accountsSpotify_accessToken.json index e548a0b..f7f2390 100644 --- a/api/src/test/resources/mappings/accountsSpotify_accessToken.json +++ b/api/src/test/resources/mappings/accountsSpotify_accessToken.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/api/token", + "urlPath": "/api/token", "headers": { "Content-Type": { "equalTo": "application/x-www-form-urlencoded" diff --git a/api/src/test/resources/mappings/setlistFm_getArtist.json b/api/src/test/resources/mappings/setlistFm_getArtist.json new file mode 100644 index 0000000..22f1a3b --- /dev/null +++ b/api/src/test/resources/mappings/setlistFm_getArtist.json @@ -0,0 +1,27 @@ +{ + "request": { + "method": "GET", + "urlPath": "/rest/1.0/artist/4e7209ee-ef02-4cb7-bdff-815b0473c27c", + "headers": { + "Accept": { + "equalTo": "application/json" + }, + "x-api-key": { + "equalTo": "TEST_API_KEY" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "mbid": "4e7209ee-ef02-4cb7-bdff-815b0473c27c", + "name": "Manel", + "sortName": "Manel", + "disambiguation": "", + "url": "https://www.setlist.fm/setlists/manel-4bd7bfce.html" + } + } +} \ No newline at end of file diff --git a/api/src/test/resources/mappings/setlistFm_getArtistSetlists.json b/api/src/test/resources/mappings/setlistFm_getArtistSetlists.json new file mode 100644 index 0000000..43ff027 --- /dev/null +++ b/api/src/test/resources/mappings/setlistFm_getArtistSetlists.json @@ -0,0 +1,186 @@ +{ + "request": { + "method": "GET", + "urlPath": "/rest/1.0/artist/4e7209ee-ef02-4cb7-bdff-815b0473c27c/setlists", + "headers": { + "Accept": { + "equalTo": "application/json" + }, + "x-api-key": { + "equalTo": "TEST_API_KEY" + } + }, + "queryParameters": { + "p": { + "equalTo": "1" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "type": "setlists", + "itemsPerPage": 20, + "page": 1, + "total": 168, + "setlist": [ + { + "id": "53be1b39", + "versionId": "g23e7883f", + "eventDate": "05-11-2022", + "lastUpdated": "2022-11-05T12:19:24.722+0000", + "artist": { + "mbid": "4e7209ee-ef02-4cb7-bdff-815b0473c27c", + "name": "Manel", + "sortName": "Manel", + "disambiguation": "", + "url": "https://www.setlist.fm/setlists/manel-4bd7bfce.html" + }, + "venue": { + "id": "3bd00ca0", + "name": "Pazo da Cultura de Pontevedra", + "city": { + "id": "3113209", + "name": "Pontevedra", + "state": "Galicia", + "stateCode": "58", + "coords": { + "lat": 42.4309957178688, + "long": -8.64435195922852 + }, + "country": { + "code": "ES", + "name": "Spain" + } + }, + "url": "https://www.setlist.fm/venue/pazo-da-cultura-de-pontevedra-pontevedra-spain-3bd00ca0.html" + }, + "sets": { + "set": [] + }, + "url": "https://www.setlist.fm/setlist/manel/2022/pazo-da-cultura-de-pontevedra-pontevedra-spain-53be1b39.html" + }, + { + "id": "4bbe1b46", + "versionId": "g23e78847", + "eventDate": "04-11-2022", + "lastUpdated": "2022-11-05T12:18:06.675+0000", + "artist": { + "mbid": "4e7209ee-ef02-4cb7-bdff-815b0473c27c", + "name": "Manel", + "sortName": "Manel", + "disambiguation": "", + "url": "https://www.setlist.fm/setlists/manel-4bd7bfce.html" + }, + "venue": { + "id": "23d7ac33", + "name": "Teatro Jofre", + "city": { + "id": "3123493", + "name": "Ferrol", + "state": "Galicia", + "stateCode": "58", + "coords": { + "lat": 43.4833333, + "long": -8.2333333 + }, + "country": { + "code": "ES", + "name": "Spain" + } + }, + "url": "https://www.setlist.fm/venue/teatro-jofre-ferrol-spain-23d7ac33.html" + }, + "sets": { + "set": [] + }, + "url": "https://www.setlist.fm/setlist/manel/2022/teatro-jofre-ferrol-spain-4bbe1b46.html" + }, + { + "id": "73b07e29", + "versionId": "g2bfaf8f6", + "eventDate": "18-09-2022", + "lastUpdated": "2022-09-19T10:56:05.000+0000", + "artist": { + "mbid": "4e7209ee-ef02-4cb7-bdff-815b0473c27c", + "name": "Manel", + "sortName": "Manel", + "disambiguation": "", + "url": "https://www.setlist.fm/setlists/manel-4bd7bfce.html" + }, + "venue": { + "id": "2bd0643e", + "name": "Plaça Corsini", + "city": { + "id": "3108288", + "name": "Tarragona", + "state": "Catalonia", + "stateCode": "56", + "coords": { + "lat": 41.1166667, + "long": 1.25 + }, + "country": { + "code": "ES", + "name": "Spain" + } + }, + "url": "https://www.setlist.fm/venue/placa-corsini-tarragona-spain-2bd0643e.html" + }, + "sets": { + "set": [ + { + "song": [ + { + "name": "En la que el Bernat se't troba" + }, + { + "name": "Per la bona gent" + } + ] + }, + { + "encore": 1, + "song": [ + { + "name": "La gent normal", + "cover": { + "mbid": "76b2e842-5e85-4c97-ab62-d5bc315595b5", + "name": "Pulp", + "sortName": "Pulp", + "disambiguation": "", + "url": "https://www.setlist.fm/setlists/pulp-bd6bdda.html" + } + }, + { + "name": "Al mar!", + "info": "J Balvin's \"Mi gente\" mashup version" + }, + { + "name": "Benvolgut" + }, + { + "name": "Mierda de ciudad", + "tape": true, + "cover": { + "mbid": "74241690-77b4-4ce7-8237-9f71a14f50b7", + "name": "Kortatu", + "sortName": "Kortatu", + "disambiguation": "", + "url": "https://www.setlist.fm/setlists/kortatu-bd6351a.html" + } + } + ] + } + ] + }, + "info": "\"Mort d'un heroi romàntic\" was written in the setlist after \"Tipus suite\", but crossed out", + "url": "https://www.setlist.fm/setlist/manel/2022/placa-corsini-tarragona-spain-73b07e29.html" + } + ] + } + } +} \ No newline at end of file diff --git a/api/src/test/resources/mappings/setlistFm_getSetlist.json b/api/src/test/resources/mappings/setlistFm_getSetlist.json index c3d7c57..a7913f1 100644 --- a/api/src/test/resources/mappings/setlistFm_getSetlist.json +++ b/api/src/test/resources/mappings/setlistFm_getSetlist.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "url": "/rest/1.0/setlist/abc12345", + "urlPath": "/rest/1.0/setlist/abc12345", "headers": { "Accept": { "equalTo": "application/json"