diff --git a/sonata-connect-run.test.sh b/sonata-connect-run.test.sh index 722fc50..74fa9cb 100644 --- a/sonata-connect-run.test.sh +++ b/sonata-connect-run.test.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker-compose -f docker-compose.test.yml up --abort-on-container-exit \ No newline at end of file +docker compose -f docker-compose.test.yml up --abort-on-container-exit \ No newline at end of file diff --git a/src/main/java/com/odeyalo/sonata/connect/service/player/handler/PlayerStateUpdatePlayCommandHandlerDelegate.java b/src/main/java/com/odeyalo/sonata/connect/service/player/handler/PlayerStateUpdatePlayCommandHandlerDelegate.java index f330a33..4677457 100644 --- a/src/main/java/com/odeyalo/sonata/connect/service/player/handler/PlayerStateUpdatePlayCommandHandlerDelegate.java +++ b/src/main/java/com/odeyalo/sonata/connect/service/player/handler/PlayerStateUpdatePlayCommandHandlerDelegate.java @@ -42,14 +42,14 @@ public Mono playOrResume(@NotNull final User user, } @NotNull - private Mono executeCommand(@NotNull final PlayCommandContext context, + private Mono executeCommand(@NotNull final PlayCommandContext playback, @NotNull final CurrentPlayerState state) { - if ( context.getContextUri() == null ) { + if ( playback.shouldBeResumed() ) { return resumePlayback(state); } - return playableItemLoader.loadPlayableItem(context.getContextUri()) + return playableItemLoader.loadPlayableItem(playback.getContextUri()) .switchIfEmpty(Mono.defer(() -> Mono.error(PlayableItemNotFoundException.defaultException()))) .flatMap(item -> play(state, item)); } diff --git a/src/main/java/com/odeyalo/sonata/connect/support/web/resolver/PlayCommandContextResolver.java b/src/main/java/com/odeyalo/sonata/connect/support/web/resolver/PlayCommandContextResolver.java index e030e0e..4ab7aff 100644 --- a/src/main/java/com/odeyalo/sonata/connect/support/web/resolver/PlayCommandContextResolver.java +++ b/src/main/java/com/odeyalo/sonata/connect/support/web/resolver/PlayCommandContextResolver.java @@ -17,6 +17,7 @@ import java.lang.reflect.Parameter; import java.util.List; +import java.util.function.Function; /** * Resolves a {@link PlayCommandContext} from the given {@link ServerWebExchange} @@ -41,25 +42,27 @@ public Mono resolveArgument(@NotNull final MethodParameter parameter, final Parameter bodyTypeParameter = resolveBodyTypeParameter(); - return readBody(MethodParameter.forParameter(bodyTypeParameter), parameter, true, bindingContext, exchange) + return readBody(MethodParameter.forParameter(bodyTypeParameter), parameter, false, bindingContext, exchange) .cast(PlayResumePlaybackRequest.class) - .map(PlayResumePlaybackRequest::getContextUri) - .flatMap(PlayCommandContextResolver::tryParseContextUri); + .flatMap(PlayCommandContextResolver::tryParseCommandRequest) + .defaultIfEmpty(PlayCommandContext.resumePlayback()) + // we need this because java can't recognize type correctly :( + .map(Function.identity()); } @NotNull - private static Mono tryParseContextUri(final String contextUriStr) { + private static Mono tryParseCommandRequest(final PlayResumePlaybackRequest playResumePlaybackRequest) { + final String contextUriStr = playResumePlaybackRequest.getContextUri(); + if ( ContextUri.isValid(contextUriStr) ) { - return Mono.just( - PlayCommandContext.from( - ContextUri.fromString(contextUriStr) - )); + return Mono.just(PlayCommandContext.from( + ContextUri.fromString(contextUriStr) + )); } - return Mono.defer( - () -> Mono.error( - new ReasonCodeAwareMalformedContextUriException("Context uri is malformed", contextUriStr) - )); + return Mono.defer(() -> Mono.error( + new ReasonCodeAwareMalformedContextUriException("Context uri is malformed", contextUriStr) + )); } @NotNull diff --git a/src/test/java/com/odeyalo/sonata/connect/controller/PlayResumePlaybackEndpointTest.java b/src/test/java/com/odeyalo/sonata/connect/controller/PlayResumePlaybackEndpointTest.java index 861f418..326a68f 100644 --- a/src/test/java/com/odeyalo/sonata/connect/controller/PlayResumePlaybackEndpointTest.java +++ b/src/test/java/com/odeyalo/sonata/connect/controller/PlayResumePlaybackEndpointTest.java @@ -86,7 +86,7 @@ public PlayableItemLoader testablePlayableItemLoader() { void shouldReturn204Status() { connectDevice(); - WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest( + WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -97,7 +97,7 @@ void shouldReturn204Status() { void shouldChangePlayerCurrentPlayableItemToProvided() { connectDevice(); - final WebTestClient.ResponseSpec ignored = sendResumeEndpointRequest( + final WebTestClient.ResponseSpec ignored = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -111,7 +111,7 @@ void shouldChangePlayerCurrentPlayableItemToProvided() { void shouldSetTrueToPlayingField() { connectDevice(); - final WebTestClient.ResponseSpec ignored = sendResumeEndpointRequest( + final WebTestClient.ResponseSpec ignored = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -123,10 +123,9 @@ void shouldSetTrueToPlayingField() { @Test void shouldUpdateCurrentlyPlayingType() { - Hooks.onOperatorDebug(); connectDevice(); - final WebTestClient.ResponseSpec ignored = sendResumeEndpointRequest( + final WebTestClient.ResponseSpec ignored = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -136,13 +135,31 @@ void shouldUpdateCurrentlyPlayingType() { .currentlyPlayingType().track(); } + @Test + void shouldResumePlaybackIfBodyWasNotSetAndPlayerHasPlayableItem() { + connectDevice(); + + final WebTestClient.ResponseSpec ignored = sendPlayOrResumeCommandRequest( + PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) + ); + + pausePlayback(); + + + sendResumeCurrentPlaybackRequest(); + + final PlayerStateDto updatedState = getCurrentState(); + + PlayerStateDtoAssert.forState(updatedState).shouldPlay(); + } + @Test void shouldReturn400BadRequestIfContextUriMalformed() { connectDevice(); final String invalidContextUri = "sonata:somethinginvalid:mikuuuu"; - final WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest(PlayResumePlaybackRequest.of(invalidContextUri)); + final WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest(PlayResumePlaybackRequest.of(invalidContextUri)); responseSpec.expectStatus().isBadRequest(); } @@ -153,7 +170,7 @@ void shouldReturnExceptionMessage() { final String invalidContextUri = "sonata:somethinginvalid:mikuuuu"; - final WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest(PlayResumePlaybackRequest.of(invalidContextUri)); + final WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest(PlayResumePlaybackRequest.of(invalidContextUri)); final ReasonCodeAwareExceptionMessage body = responseSpec.expectBody(ReasonCodeAwareExceptionMessage.class) .returnResult().getResponseBody(); @@ -168,7 +185,7 @@ void shouldReturnExceptionReasonCode() { final String invalidContextUri = "sonata:somethinginvalid:mikuuuu"; - final WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest(PlayResumePlaybackRequest.of(invalidContextUri)); + final WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest(PlayResumePlaybackRequest.of(invalidContextUri)); final ReasonCodeAwareExceptionMessage body = responseSpec.expectBody(ReasonCodeAwareExceptionMessage.class).returnResult().getResponseBody(); @@ -180,7 +197,7 @@ void shouldReturnExceptionReasonCode() { void shouldDoNothingIfPlayerWasJustCreated() { final PlayerStateDto beforeRequest = getCurrentState(); - final WebTestClient.ResponseSpec ignored = sendResumeCurrentPlaybackEndpointRequest(); + final WebTestClient.ResponseSpec ignored = sendResumeCurrentPlaybackRequest(); final PlayerStateDto afterRequest = getCurrentState(); @@ -190,7 +207,7 @@ void shouldDoNothingIfPlayerWasJustCreated() { @Test void shouldReturn400StatusForEmptyState() { - WebTestClient.ResponseSpec responseSpec = sendResumeCurrentPlaybackEndpointRequest(); + WebTestClient.ResponseSpec responseSpec = sendResumeCurrentPlaybackRequest(); responseSpec.expectStatus().isBadRequest(); } @@ -202,7 +219,7 @@ class EmptyDeviceListTest { @Test void shouldReturnBadRequest() { - final WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest( + final WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -211,7 +228,7 @@ void shouldReturnBadRequest() { @Test void shouldReturnExceptionMessageInBody() { - final WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest( + final WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -225,7 +242,7 @@ void shouldReturnExceptionMessageInBody() { @Test void shouldReturnExceptionReasonInBody() { - final WebTestClient.ResponseSpec responseSpec = sendResumeEndpointRequest( + final WebTestClient.ResponseSpec responseSpec = sendPlayOrResumeCommandRequest( PlayResumePlaybackRequest.of(EXISTING_TRACK_CONTEXT_URI) ); @@ -234,21 +251,25 @@ void shouldReturnExceptionReasonInBody() { ReasonCodeAwareExceptionMessageAssert.forMessage(body) .reasonCode().isEqualTo("no_active_device"); } - } + } private void connectDevice() { final ConnectDeviceRequest connectDeviceRequest = ConnectDeviceRequestFaker.create().get(); testOperations.connectDevice(VALID_ACCESS_TOKEN, connectDeviceRequest); } + private void pausePlayback() { + testOperations.pause(VALID_ACCESS_TOKEN); + } + @NotNull - private WebTestClient.ResponseSpec sendResumeCurrentPlaybackEndpointRequest() { - return sendResumeEndpointRequest(null); + private WebTestClient.ResponseSpec sendResumeCurrentPlaybackRequest() { + return sendPlayOrResumeCommandRequest(null); } @NotNull - private WebTestClient.ResponseSpec sendResumeEndpointRequest(@Nullable PlayResumePlaybackRequest body) { + private WebTestClient.ResponseSpec sendPlayOrResumeCommandRequest(@Nullable PlayResumePlaybackRequest body) { final WebTestClient.RequestBodySpec builder = webTestClient.put() .uri("/player/play") diff --git a/src/test/java/testing/shared/SonataTestHttpOperations.java b/src/test/java/testing/shared/SonataTestHttpOperations.java index c209e36..5a08929 100644 --- a/src/test/java/testing/shared/SonataTestHttpOperations.java +++ b/src/test/java/testing/shared/SonataTestHttpOperations.java @@ -18,6 +18,8 @@ public interface SonataTestHttpOperations { void playOrResumePlayback(String authorizationHeaderValue, PlayResumePlaybackRequest body); + void pause(String validAccessToken); + void changeShuffle(String authorizationHeaderValue, boolean shuffleMode); void switchDevices(String authorizationHeaderValue, DeviceSwitchRequest body); diff --git a/src/test/java/testing/shared/WebTestClientSonataTestHttpOperations.java b/src/test/java/testing/shared/WebTestClientSonataTestHttpOperations.java index 5b7c0da..724af06 100644 --- a/src/test/java/testing/shared/WebTestClientSonataTestHttpOperations.java +++ b/src/test/java/testing/shared/WebTestClientSonataTestHttpOperations.java @@ -53,6 +53,15 @@ public void playOrResumePlayback(String authorizationHeaderValue, PlayResumePlay .exchange(); } + @Override + public void pause(String authorizationHeaderValue) { + webTestClient.put() + .uri("/player/pause") + .header(HttpHeaders.AUTHORIZATION, authorizationHeaderValue) + .exchange() + .expectStatus().isNoContent(); + } + @Override public void changeShuffle(String authorizationHeaderValue, boolean shuffleMode) { webTestClient.put()