diff --git a/apollo-api/src/main/java/com/spotify/apollo/Response.java b/apollo-api/src/main/java/com/spotify/apollo/Response.java index 0b8e740b5..7220695da 100644 --- a/apollo-api/src/main/java/com/spotify/apollo/Response.java +++ b/apollo-api/src/main/java/com/spotify/apollo/Response.java @@ -90,6 +90,42 @@ default

Response

mapPayload(Function mapFunction) : withPayload(mapFunction.apply(payload().get())); } + /** + * Maps the contained payload if it exists, otherwise does nothing except changing the type + * of the response. + * + * The new response contents will be merged with this response using {@link #merge(Response)}. + * + * @param mapFunction The function for mapping the payload + * @param

The type of the mapped payload + * @return A response with a converted payload + */ + default

Response

flatMapPayload(Function> mapFunction) { + //noinspection unchecked + return !payload().isPresent() + ? (Response

) this + : merge(mapFunction.apply(payload().get())); + } + + /** + * Merge another response with this response. + * + * The payload of the other response will override the payload in this response, + * even if not present, in which case it will reset the payload. + * + * Headers keys will be merged with headers from the other response taking precedence. + * + * The status of the other response will always be ignored. + * + * @param other The other response to merge with this + * @param

The type of the other response payload + * @return A merged response + */ + default

Response

merge(Response other) { + return withPayload(other.payload().orElse(null)) + .withHeaders(other.headers()); + } + /** * Returns a typed 200 OK {@link Response}. * diff --git a/apollo-api/src/test/java/com/spotify/apollo/ResponsePayloadMapTest.java b/apollo-api/src/test/java/com/spotify/apollo/ResponsePayloadMapTest.java index 28c9254a3..6770bc40a 100644 --- a/apollo-api/src/test/java/com/spotify/apollo/ResponsePayloadMapTest.java +++ b/apollo-api/src/test/java/com/spotify/apollo/ResponsePayloadMapTest.java @@ -57,6 +57,13 @@ public void mapsPayload(Response input) throws Exception { } } + @Theory + public void removePayloadIfNull(Response input) throws Exception { + Response intResponse = input.mapPayload(ignored -> null); + + assertFalse(intResponse.payload().isPresent()); + } + @Theory public void preserveStatusAndHeaderOnMap(Response input) throws Exception { Response intResponse = input.mapPayload(String::length); @@ -64,4 +71,43 @@ public void preserveStatusAndHeaderOnMap(Response input) throws Exceptio assertThat(intResponse.status(), is(Status.IM_A_TEAPOT)); assertThat(intResponse.headers(), hasEntry("foo", "bar")); } + + @Theory + public void mapsPayloadOnFlatMap(Response input) throws Exception { + Response intResponse = input.flatMapPayload( + str -> Response.forPayload(str.length())); + + if (input.payload().isPresent()) { + assertThat(intResponse.payload().get().intValue(), is(5)); + } else { + assertFalse(intResponse.payload().isPresent()); + } + } + + @Theory + public void mergesHeadersOnFlatMap(Response input) throws Exception { + Response intResponse = input.flatMapPayload( + str -> Response.forPayload(str.length()) + .withHeader("baz", str)); + + assertThat(intResponse.headers(), hasEntry("foo", "bar")); + if (input.payload().isPresent()) { + assertThat(intResponse.headers(), hasEntry("baz", "hello")); + } + } + + @Theory + public void preservesStatusOnFlatMap(Response input) throws Exception { + Response intResponse = input.flatMapPayload( + ignored -> Response.forStatus(Status.GONE)); + + assertThat(intResponse.status(), is(Status.IM_A_TEAPOT)); + } + + @Theory + public void removePayloadIfNotSetOnFlatMap(Response input) throws Exception { + Response intResponse = input.flatMapPayload(ignored -> Response.ok()); + + assertFalse(intResponse.payload().isPresent()); + } }