diff --git a/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java b/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java index c2184c3e..197f2903 100644 --- a/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java +++ b/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java @@ -3,12 +3,14 @@ package org.springframework.hateoas.client; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.hateoas.config.HypermediaRestTemplateConfigurer; +import org.springframework.hateoas.config.HypermediaWebClientConfigurer; import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; -// tag::code[] @Configuration public class HypermediaConfiguration { + // tag::rest-template[] /** * Use the {@link HypermediaRestTemplateConfigurer} to configure a {@link RestTemplate}. */ @@ -16,5 +18,12 @@ public class HypermediaConfiguration { RestTemplate hypermediaRestTemplate(HypermediaRestTemplateConfigurer configurer) { // <1> return configurer.registerHypermediaTypes(new RestTemplate()); // <2> } + // end::rest-template[] + + // tag::web-client[] + @Bean + WebClient.Builder hypermediaWebClient(HypermediaWebClientConfigurer configurer) { // <1> + return configurer.registerHypermediaTypes(WebClient.builder()); // <2> + } + // end::web-client[] } -// end::code[] diff --git a/src/main/asciidoc/client.adoc b/src/main/asciidoc/client.adoc index fc03a847..aa3d38be 100644 --- a/src/main/asciidoc/client.adoc +++ b/src/main/asciidoc/client.adoc @@ -91,6 +91,46 @@ assertThat(link.getHref(), is("/foo/bar")); ---- ==== +[[client.web-client]] +== Configuring WebClient instances + +If you need configure a `WebClient` to speak hypermedia, it's easy. Get a hold of the `HypermediaWebClientConfigurer` as shown below: + +.Configuring a `WebClient` yourself +==== +[source, java, tabsize=0, indent=0] +---- +include::{base-dir}/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java[tag=web-client] +---- +<1> Inside your `@Configuration` class, get a copy of the `HypermediaWebClientConfigurer` bean Spring HATEOAS registers. +<2> After creating a `WebClient.Builder`, use the configurer to register hypermedia types. +==== + +NOTE: What `HypermediaWebClientConfigurer` does it register all the right encoders and decoders with a `WebClient.Builder`. To make use of it, +you need to inject the builder somewhere into your application, and run the `build()` method to produce a `WebClient`. + +If you're using Spring Boot, there is another way: the `WebClientCustomizer`. + +.Letting Spring Boot configure things +==== +[source,java] +---- +@Bean // <4> +WebClientCustomizer hypermediaWebClientCustomizer(HypermediaWebClientConfigurer configurer) { // <1> + return webClientBuilder -> { // <2> + configurer.registerHypermediaTypes(webClientBuilder); // <3> + }; +} +---- +<1> When creating a Spring bean, request a copy of Spring HATEOAS's `HypermediaRestTemplateConfigurer` bean. +<2> Use a Java 8 lambda expression to define a `WebClientCustomizer`. +<3> Inside the function call, apply the `registerHypermediaTypes` method. +<4> Return the whole thing as a Spring bean so Spring Boot can pick it up and apply it to its autoconfigured `WebClient.Builder` bean. +==== + +At this stage, whenever you need a concrete `WebClient`, simply inject `WebClient.Builder` into your code, and use `build()`. The `WebClient` instance +will be able to interact using hypermedia. + [[client.web-test-client]] == Configuring `WebTestClient` Instances @@ -110,7 +150,7 @@ include::{base-dir}/src/test/java/org/springframework/hateoas/config/HypermediaW <4> After getting the "body" in Spring HATEOAS format, assert against it! ==== -IMPORTANT: `WebTestClient` is an immutable value type, so you can't alter it in place. `WebClientConfigurer` returns a mutated +IMPORTANT: `WebTestClient` is an immutable value type, so you can't alter it in place. `HypermediaWebClientConfigurer` returns a mutated variant that you must then capture to use it. If you are using Spring Boot, there are additional options, like this: @@ -151,11 +191,11 @@ There are many other ways to fashion test cases. `WebTestClient` can be bound to If you want to create your own copy of `RestTemplate`, configured to speak hypermedia, you can use the `HypermediaRestTemplateConfigurer`: -.Configuring a `RestTemplate` yourself +.Configuring `RestTemplate` yourself ==== -[source, java] +[source, java, tabsize=0, indent=0] ---- -include::{base-dir}/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java[tag=code] +include::{base-dir}/src/docs/java/org/springframework/hateoas/client/HypermediaConfiguration.java[tag=rest-template] ---- <1> Inside your `@Configuration` class, get a copy of the `HypermediaRestTemplateConfigurer` bean Spring HATEOAS registers. <2> After creating a `RestTemplate`, use the configurer to apply hypermedia types. @@ -181,7 +221,7 @@ To register hypermedia-based message converters, add the following to your code: [source,java] ---- @Bean // <4> -RestTemplateCustomizer hypermediaRestTemplatCustomizer(HypermediaRestTemplateConfigurer configurer) { // <1> +RestTemplateCustomizer hypermediaRestTemplateCustomizer(HypermediaRestTemplateConfigurer configurer) { // <1> return restTemplate -> { // <2> configurer.registerHypermediaTypes(restTemplate); // <3> }; @@ -193,30 +233,5 @@ RestTemplateCustomizer hypermediaRestTemplatCustomizer(HypermediaRestTemplateCon <4> Return the whole thing as a Spring bean so Spring Boot can pick it up and apply it to its autoconfigured `RestTemplateBuilder`. ==== -Assuming you added that `RestTemplateCustomizer` bean definition, this is all you must do to get a `RestTemplate` with hypermedia support: - -.Injecting `RestTemplateBuilder` into your service -==== -[source,java] ----- -@Service -public class SampleService { - - private RestTemplateBuilder restTemplateBuilder; - - public SampleService(RestTemplateBuilder restTemplateBuilder) { // <1> - this.restTemplateBuilder = restTemplateBuilder; - } - - void doSomething() { - RestTemplate template = restTemplateBuilder.build(); // <2> - - // Your template is now configured to speak hypermedia! - } -} ----- -<1> Use *constructor injection* to get a hold of Spring Boot's `RestTemplateBuilder`. -<2> When you need a `RestTemplate`, invoke `build()`. The instance will have tapped your `RestTemplateCustomizer` code and registered support. -==== - -That's all it takes to get `RestTemplate` hypermedia support. +At this stage, whenever you need a concrete `RestTemplate`, simply inject `RestTemplateBuilder` into your code, and use `build()`. The `RestTemplate` instance +will be able to interact using hypermedia. diff --git a/src/main/java/org/springframework/hateoas/config/HypermediaRestTemplateConfigurer.java b/src/main/java/org/springframework/hateoas/config/HypermediaRestTemplateConfigurer.java index f53a25a2..f29422a9 100644 --- a/src/main/java/org/springframework/hateoas/config/HypermediaRestTemplateConfigurer.java +++ b/src/main/java/org/springframework/hateoas/config/HypermediaRestTemplateConfigurer.java @@ -12,6 +12,11 @@ public class HypermediaRestTemplateConfigurer { private final WebConverters converters; + /** + * Creates a new {@link HypermediaRestTemplateConfigurer} using the {@link WebConverters}. + * + * @param converters + */ HypermediaRestTemplateConfigurer(WebConverters converters) { this.converters = converters; } @@ -20,7 +25,7 @@ public class HypermediaRestTemplateConfigurer { * Insert hypermedia-aware message converters in front of any other existing message converters. * * @param template - * @return + * @return {@link RestTemplate} capable of speaking hypermedia. */ public RestTemplate registerHypermediaTypes(RestTemplate template) { diff --git a/src/main/java/org/springframework/hateoas/config/HypermediaWebClientConfigurer.java b/src/main/java/org/springframework/hateoas/config/HypermediaWebClientConfigurer.java new file mode 100644 index 00000000..1d96db50 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/config/HypermediaWebClientConfigurer.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.hateoas.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.codec.ClientCodecConfigurer; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.util.Assert; +import org.springframework.util.MimeType; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.List; +import java.util.function.Consumer; + +/** + * Assembles {@link Jackson2JsonEncoder}s and {@link Jackson2JsonDecoder}s needed to wire a {@link WebClient} with + * hypermedia support. + * + * @author Greg Turnquist + * @author Oliver Drotbohm + * @since 1.1 + */ +public class HypermediaWebClientConfigurer { + + Consumer configurer; + + /** + * Creates a new {@link HypermediaWebClientConfigurer} for the given {@link ObjectMapper} and + * {@link HypermediaMappingInformation}s. + * + * @param mapper must not be {@literal null}. + * @param hypermediaTypes must not be {@literal null}. + */ + HypermediaWebClientConfigurer(ObjectMapper mapper, List hypermediaTypes) { + + Assert.notNull(mapper, "ObjectMapper must not be null!"); + Assert.notNull(hypermediaTypes, "HypermediaMappingInformations must not be null!"); + + this.configurer = clientCodecConfigurer -> hypermediaTypes.forEach(hypermediaType -> { + + ObjectMapper objectMapper = hypermediaType.configureObjectMapper(mapper.copy()); + MimeType[] mimeTypes = hypermediaType.getMediaTypes().toArray(new MimeType[0]); + + clientCodecConfigurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonEncoder(objectMapper, mimeTypes)); + clientCodecConfigurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonDecoder(objectMapper, mimeTypes)); + }); + } + + /** + * Apply the proper {@link Jackson2JsonEncoder}s and {@link Jackson2JsonDecoder}s to this {@link WebClient.Builder}. + * + * @param builder + * @return {@link WebClient.Builder} registered to handle hypermedia types. + */ + public WebClient.Builder registerHypermediaTypes(WebClient.Builder builder) { + return builder.codecs(this.configurer); + } +} diff --git a/src/main/java/org/springframework/hateoas/config/WebClientConfigurer.java b/src/main/java/org/springframework/hateoas/config/WebClientConfigurer.java index 6da0d8a4..05d61474 100644 --- a/src/main/java/org/springframework/hateoas/config/WebClientConfigurer.java +++ b/src/main/java/org/springframework/hateoas/config/WebClientConfigurer.java @@ -1,39 +1,10 @@ -/* - * Copyright 2019-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.springframework.hateoas.config; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import org.springframework.context.annotation.Configuration; -import org.springframework.core.codec.Decoder; -import org.springframework.core.codec.Encoder; -import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; -import org.springframework.http.codec.ClientCodecConfigurer; -import org.springframework.http.codec.CodecConfigurer.CustomCodecs; -import org.springframework.http.codec.json.AbstractJackson2Decoder; -import org.springframework.http.codec.json.Jackson2JsonDecoder; -import org.springframework.http.codec.json.Jackson2JsonEncoder; -import org.springframework.util.Assert; -import org.springframework.util.MimeType; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.web.reactive.function.client.ExchangeStrategies; import org.springframework.web.reactive.function.client.WebClient; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; /** * Assembles {@link ExchangeStrategies} needed to wire a {@link WebClient} with hypermedia support. @@ -41,11 +12,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; * @author Greg Turnquist * @author Oliver Drotbohm * @since 1.0 + * @deprecated Migrate to {@link HypermediaWebClientConfigurer} and it's {@link WebClient.Builder}-oriented + * approach. */ -@Configuration +@Deprecated public class WebClientConfigurer { - Consumer configurer; + private final HypermediaWebClientConfigurer hypermediaWebClientConfigurer; /** * Creates a new {@link WebClientConfigurer} for the given {@link ObjectMapper} and @@ -55,29 +28,7 @@ public class WebClientConfigurer { * @param hypermediaTypes must not be {@literal null}. */ public WebClientConfigurer(ObjectMapper mapper, List hypermediaTypes) { - - Assert.notNull(mapper, "ObjectMapper must not be null!"); - Assert.notNull(hypermediaTypes, "HypermediaMappingInformations must not be null!"); - - List> encoders = new ArrayList<>(); - List decoders = new ArrayList<>(); - - hypermediaTypes.forEach(hypermedia -> { - - ObjectMapper objectMapper = hypermedia.configureObjectMapper(mapper.copy()); - MimeType[] mimeTypes = hypermedia.getMediaTypes().toArray(new MimeType[0]); - - encoders.add(new Jackson2JsonEncoder(objectMapper, mimeTypes)); - decoders.add(new Jackson2JsonDecoder(objectMapper, mimeTypes)); - }); - - this.configurer = it -> { - - CustomCodecs codecs = it.customCodecs(); - - encoders.forEach(codecs::registerWithDefaultConfig); - decoders.forEach(codecs::registerWithDefaultConfig); - }; + this.hypermediaWebClientConfigurer = new HypermediaWebClientConfigurer(mapper, hypermediaTypes); } /** @@ -88,7 +39,7 @@ public class WebClientConfigurer { public ExchangeStrategies hypermediaExchangeStrategies() { return ExchangeStrategies.builder() // - .codecs(configurer) // + .codecs(this.hypermediaWebClientConfigurer.configurer) // .build(); } @@ -99,9 +50,6 @@ public class WebClientConfigurer { * @return mutated webClient with hypermedia support. */ public WebClient registerHypermediaTypes(WebClient webClient) { - - return webClient.mutate() // - .codecs(configurer) // - .build(); + return this.hypermediaWebClientConfigurer.registerHypermediaTypes(webClient.mutate()).build(); } } diff --git a/src/main/java/org/springframework/hateoas/config/WebClientHateoasConfiguration.java b/src/main/java/org/springframework/hateoas/config/WebClientHateoasConfiguration.java index 16124f80..269d839f 100644 --- a/src/main/java/org/springframework/hateoas/config/WebClientHateoasConfiguration.java +++ b/src/main/java/org/springframework/hateoas/config/WebClientHateoasConfiguration.java @@ -38,21 +38,20 @@ import java.util.List; class WebClientHateoasConfiguration { @Bean - WebClientConfigurer webClientConfigurer(ObjectProvider mapper, + HypermediaWebClientConfigurer webClientConfigurer(ObjectProvider mapper, List hypermediaTypes) { - return new WebClientConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes); + return new HypermediaWebClientConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes); } @Bean @Lazy - HypermediaWebTestClientConfigurer webTestClientConfigurer(ObjectProvider mapper, - List hypermediaTypes) { + HypermediaWebTestClientConfigurer webTestClientConfigurer(ObjectProvider mapper, + List hypermediaTypes) { return new HypermediaWebTestClientConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes); } @Bean - static HypermediaWebClientBeanPostProcessor webClientBeanPostProcessor( - ObjectProvider configurer) { + static HypermediaWebClientBeanPostProcessor webClientBeanPostProcessor(HypermediaWebClientConfigurer configurer) { return new HypermediaWebClientBeanPostProcessor(configurer); } @@ -66,7 +65,7 @@ class WebClientHateoasConfiguration { @RequiredArgsConstructor static class HypermediaWebClientBeanPostProcessor implements BeanPostProcessor { - private final ObjectProvider configurer; + private final HypermediaWebClientConfigurer configurer; /* * (non-Javadoc) @@ -76,8 +75,8 @@ class WebClientHateoasConfiguration { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof WebClient) { - return this.configurer.getObject().registerHypermediaTypes((WebClient) bean); + if (WebClient.class.isInstance(bean)) { + return this.configurer.registerHypermediaTypes(((WebClient) bean).mutate()).build(); } return bean; diff --git a/src/test/java/org/springframework/hateoas/ArchitectureTest.java b/src/test/java/org/springframework/hateoas/ArchitectureTest.java index e2ab4b63..bff2c9fc 100644 --- a/src/test/java/org/springframework/hateoas/ArchitectureTest.java +++ b/src/test/java/org/springframework/hateoas/ArchitectureTest.java @@ -68,6 +68,7 @@ class ArchitectureTest { .and().resideOutsideOfPackages("..reactive") // .and().haveSimpleNameNotStartingWith("WebFlux") // .and().haveSimpleNameNotStartingWith("WebClient") // + .and().haveSimpleNameNotStartingWith("HypermediaWebClient") // .and().haveSimpleNameNotStartingWith("HypermediaWebTestClient") // .should().dependOnClassesThat(areSpringFrameworkClassesWithReactiveDependency) // .check(classes); diff --git a/src/test/java/org/springframework/hateoas/config/CustomHypermediaWebFluxTest.java b/src/test/java/org/springframework/hateoas/config/CustomHypermediaWebFluxTest.java index 3c321759..df8b52b9 100644 --- a/src/test/java/org/springframework/hateoas/config/CustomHypermediaWebFluxTest.java +++ b/src/test/java/org/springframework/hateoas/config/CustomHypermediaWebFluxTest.java @@ -52,7 +52,7 @@ class CustomHypermediaWebFluxTest { ctx.register(TestConfig.class); ctx.refresh(); - WebClientConfigurer webClientConfigurer = ctx.getBean(WebClientConfigurer.class); + HypermediaWebClientConfigurer webClientConfigurer = ctx.getBean(HypermediaWebClientConfigurer.class); this.testClient = WebTestClient.bindToApplicationContext(ctx).build() // .mutate() // diff --git a/src/test/java/org/springframework/hateoas/config/HypermediaWebClientConfigurerTest.java b/src/test/java/org/springframework/hateoas/config/HypermediaWebClientConfigurerTest.java new file mode 100644 index 00000000..0694ac49 --- /dev/null +++ b/src/test/java/org/springframework/hateoas/config/HypermediaWebClientConfigurerTest.java @@ -0,0 +1,101 @@ +package org.springframework.hateoas.config; + +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Bean; +import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; +import org.springframework.http.MediaType; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.hateoas.MediaTypes.*; +import static org.springframework.hateoas.support.ContextTester.*; + +public class HypermediaWebClientConfigurerTest { + + private static MediaType FRODO_JSON = MediaType.parseMediaType("application/frodo+json"); + + @Test // #1224 + void webClientConfigurerHandlesSingleHypermediaType() { + + withContext(HalConfig.class, context -> { + + HypermediaWebClientConfigurer configurer = context.getBean(HypermediaWebClientConfigurer.class); + + WebClient webClient = configurer.registerHypermediaTypes(WebClient.builder()).build(); + + assertThat(exchangeStrategies(webClient).messageReaders()) + .flatExtracting(HttpMessageReader::getReadableMediaTypes) // + .contains(HAL_JSON) // + .doesNotContain(HAL_FORMS_JSON, COLLECTION_JSON, UBER_JSON); + }); + } + + @Test // #1224 + void webClientConfigurerHandlesMultipleHypermediaTypes() { + + withContext(AllHypermediaConfig.class, context -> { + + HypermediaWebClientConfigurer configurer = context.getBean(HypermediaWebClientConfigurer.class); + + WebClient webClient = configurer.registerHypermediaTypes(WebClient.builder()).build(); + + assertThat(exchangeStrategies(webClient).messageReaders()) + .flatExtracting(HttpMessageReader::getReadableMediaTypes) // + .contains(HAL_JSON, HAL_FORMS_JSON, COLLECTION_JSON, UBER_JSON); + }); + } + + @Test // #1224 + void webClientConfigurerHandlesCustomHypermediaTypes() { + + withContext(CustomHypermediaConfig.class, context -> { + + HypermediaWebClientConfigurer configurer = context.getBean(HypermediaWebClientConfigurer.class); + + WebClient webClient = configurer.registerHypermediaTypes(WebClient.builder()).build(); + + assertThat(exchangeStrategies(webClient).messageReaders()) + .flatExtracting(HttpMessageReader::getReadableMediaTypes) // + .contains(HAL_JSON, FRODO_JSON) // + .doesNotContain(HAL_FORMS_JSON, COLLECTION_JSON, UBER_JSON); + }); + } + + /** + * Extract the {@link ExchangeStrategies} from a {@link WebTestClient} to assert it has the proper message readers and + * writers. + * + * @param webClient + * @return + */ + private static ExchangeStrategies exchangeStrategies(WebClient webClient) { + + return (ExchangeStrategies) ReflectionTestUtils + .getField(ReflectionTestUtils.getField(webClient, "exchangeFunction"), "strategies"); + } + + @EnableHypermediaSupport(type = HypermediaType.HAL) + static class HalConfig { + + } + + @EnableHypermediaSupport( + type = { HypermediaType.HAL, HypermediaType.HAL_FORMS, HypermediaType.COLLECTION_JSON, HypermediaType.UBER }) + static class AllHypermediaConfig { + + } + + static class CustomHypermediaConfig extends HalConfig { + + @Bean + HypermediaMappingInformation hypermediaMappingInformation() { + return () -> Collections.singletonList(FRODO_JSON); + } + } +} diff --git a/src/test/java/org/springframework/hateoas/config/HypermediaWebFluxConfigurerTest.java b/src/test/java/org/springframework/hateoas/config/HypermediaWebFluxConfigurerTest.java index 8db90301..f523cbfa 100644 --- a/src/test/java/org/springframework/hateoas/config/HypermediaWebFluxConfigurerTest.java +++ b/src/test/java/org/springframework/hateoas/config/HypermediaWebFluxConfigurerTest.java @@ -15,18 +15,6 @@ */ package org.springframework.hateoas.config; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -50,6 +38,17 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.config.EnableWebFlux; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*; /** * @author Greg Turnquist @@ -67,10 +66,9 @@ class HypermediaWebFluxConfigurerTest { ctx.register(context); ctx.refresh(); - WebClientConfigurer webClientConfigurer = ctx.getBean(WebClientConfigurer.class); + HypermediaWebTestClientConfigurer configurer = ctx.getBean(HypermediaWebTestClientConfigurer.class); - this.testClient = WebTestClient.bindToApplicationContext(ctx).build().mutate() - .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()).build(); + this.testClient = WebTestClient.bindToApplicationContext(ctx).build().mutateWith(configurer); } /** diff --git a/src/test/java/org/springframework/hateoas/config/HypermediaWebTestClientConfigurerTest.java b/src/test/java/org/springframework/hateoas/config/HypermediaWebTestClientConfigurerTest.java index 961cd07b..b2d04c45 100644 --- a/src/test/java/org/springframework/hateoas/config/HypermediaWebTestClientConfigurerTest.java +++ b/src/test/java/org/springframework/hateoas/config/HypermediaWebTestClientConfigurerTest.java @@ -97,7 +97,7 @@ public class HypermediaWebTestClientConfigurerTest { WebTestClient client = WebTestClient.bindToApplicationContext(context).build().mutateWith(configurer); // <2> // Exercise the controller. - client.get().uri("http://localhost/employees") // + client.get().uri("http://localhost/employees").accept(HAL_JSON) // .exchange() // .expectStatus().isOk() // .expectBody(new TypeReferences.CollectionModelType>() {}) // <3> diff --git a/src/test/java/org/springframework/hateoas/mediatype/alps/AlpsWebFluxIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/alps/AlpsWebFluxIntegrationTest.java index 089e0142..d900eaa9 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/alps/AlpsWebFluxIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/alps/AlpsWebFluxIntegrationTest.java @@ -15,11 +15,6 @@ */ package org.springframework.hateoas.mediatype.alps; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*; - -import reactor.test.StepVerifier; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -28,12 +23,16 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.config.EnableHypermediaSupport; -import org.springframework.hateoas.config.WebClientConfigurer; +import org.springframework.hateoas.config.HypermediaWebTestClientConfigurer; import org.springframework.hateoas.support.WebFluxEmployeeController; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.config.EnableWebFlux; +import reactor.test.StepVerifier; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*; /** * @author Greg Turnquist @@ -97,12 +96,8 @@ public class AlpsWebFluxIntegrationTest { } @Bean - WebTestClient webTestClient(WebClientConfigurer webClientConfigurer, ApplicationContext ctx) { - - return WebTestClient.bindToApplicationContext(ctx).build() // - .mutate() // - .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()) // - .build(); + WebTestClient webTestClient(HypermediaWebTestClientConfigurer configurer, ApplicationContext ctx) { + return WebTestClient.bindToApplicationContext(ctx).build().mutateWith(configurer); } } diff --git a/src/test/java/org/springframework/hateoas/mediatype/collectionjson/CollectionJsonWebFluxIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/collectionjson/CollectionJsonWebFluxIntegrationTest.java index dbb4bb46..c2633098 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/collectionjson/CollectionJsonWebFluxIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/collectionjson/CollectionJsonWebFluxIntegrationTest.java @@ -15,11 +15,6 @@ */ package org.springframework.hateoas.mediatype.collectionjson; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.collection.IsCollectionWithSize.*; -import static org.springframework.hateoas.support.JsonPathUtils.*; -import static org.springframework.hateoas.support.MappingUtils.*; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,7 +26,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; -import org.springframework.hateoas.config.WebClientConfigurer; +import org.springframework.hateoas.config.HypermediaWebTestClientConfigurer; import org.springframework.hateoas.support.WebFluxEmployeeController; import org.springframework.http.HttpHeaders; import org.springframework.test.context.ContextConfiguration; @@ -40,6 +35,11 @@ import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.config.EnableWebFlux; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.collection.IsCollectionWithSize.*; +import static org.springframework.hateoas.support.JsonPathUtils.*; +import static org.springframework.hateoas.support.MappingUtils.*; + /** * @author Greg Turnquist */ @@ -189,12 +189,8 @@ class CollectionJsonWebFluxIntegrationTest { } @Bean - WebTestClient webTestClient(WebClientConfigurer webClientConfigurer, ApplicationContext ctx) { - - return WebTestClient.bindToApplicationContext(ctx).build() // - .mutate() // - .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()) // - .build(); + WebTestClient webTestClient(HypermediaWebTestClientConfigurer configurer, ApplicationContext ctx) { + return WebTestClient.bindToApplicationContext(ctx).build().mutateWith(configurer); } } } diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsWebFluxIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsWebFluxIntegrationTest.java index 721fe970..2c085d64 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsWebFluxIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsWebFluxIntegrationTest.java @@ -15,13 +15,6 @@ */ package org.springframework.hateoas.mediatype.hal.forms; -import static org.assertj.core.api.Assertions.*; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.collection.IsCollectionWithSize.*; -import static org.springframework.hateoas.support.JsonPathUtils.*; - -import java.net.URI; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,7 +26,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; -import org.springframework.hateoas.config.WebClientConfigurer; +import org.springframework.hateoas.config.HypermediaWebTestClientConfigurer; import org.springframework.hateoas.mediatype.problem.Problem; import org.springframework.hateoas.support.MappingUtils; import org.springframework.hateoas.support.WebFluxEmployeeController; @@ -45,6 +38,13 @@ import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.config.EnableWebFlux; +import java.net.URI; + +import static org.assertj.core.api.Assertions.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.collection.IsCollectionWithSize.*; +import static org.springframework.hateoas.support.JsonPathUtils.*; + /** * @author Greg Turnquist */ @@ -136,7 +136,8 @@ class HalFormsWebFluxIntegrationTest { @Test // #786 void problemReturningControllerMethod() { - Problem problem = this.testClient.get().uri("http://localhost/employees/problem").accept(MediaTypes.HTTP_PROBLEM_DETAILS_JSON) // + Problem problem = this.testClient.get().uri("http://localhost/employees/problem") + .accept(MediaTypes.HTTP_PROBLEM_DETAILS_JSON) // .exchange() // .expectStatus().isBadRequest() // .expectHeader().contentType(MediaTypes.HTTP_PROBLEM_DETAILS_JSON) // @@ -161,10 +162,8 @@ class HalFormsWebFluxIntegrationTest { } @Bean - WebTestClient webTestClient(WebClientConfigurer webClientConfigurer, ApplicationContext ctx) { - - return WebTestClient.bindToApplicationContext(ctx).build().mutate() - .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()).build(); + WebTestClient webTestClient(HypermediaWebTestClientConfigurer configurer, ApplicationContext ctx) { + return WebTestClient.bindToApplicationContext(ctx).build().mutateWith(configurer); } } } diff --git a/src/test/java/org/springframework/hateoas/mediatype/uber/UberWebFluxIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/uber/UberWebFluxIntegrationTest.java index 9783654c..c7c563c6 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/uber/UberWebFluxIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/uber/UberWebFluxIntegrationTest.java @@ -15,11 +15,6 @@ */ package org.springframework.hateoas.mediatype.uber; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.collection.IsCollectionWithSize.*; -import static org.springframework.hateoas.support.JsonPathUtils.*; -import static org.springframework.hateoas.support.MappingUtils.*; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,7 +26,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; -import org.springframework.hateoas.config.WebClientConfigurer; +import org.springframework.hateoas.config.HypermediaWebTestClientConfigurer; import org.springframework.hateoas.support.WebFluxEmployeeController; import org.springframework.http.HttpHeaders; import org.springframework.test.context.ContextConfiguration; @@ -40,6 +35,11 @@ import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.config.EnableWebFlux; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.collection.IsCollectionWithSize.*; +import static org.springframework.hateoas.support.JsonPathUtils.*; +import static org.springframework.hateoas.support.MappingUtils.*; + /** * @author Greg Turnquist */ @@ -247,12 +247,8 @@ class UberWebFluxIntegrationTest { } @Bean - WebTestClient webTestClient(WebClientConfigurer webClientConfigurer, ApplicationContext ctx) { - - return WebTestClient.bindToApplicationContext(ctx).build() // - .mutate() // - .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()) // - .build(); + WebTestClient webTestClient(HypermediaWebTestClientConfigurer configurer, ApplicationContext ctx) { + return WebTestClient.bindToApplicationContext(ctx).build().mutateWith(configurer); } } } diff --git a/src/test/java/org/springframework/hateoas/server/reactive/HypermediaWebFilterTest.java b/src/test/java/org/springframework/hateoas/server/reactive/HypermediaWebFilterTest.java index a9a0f498..62eb44fa 100644 --- a/src/test/java/org/springframework/hateoas/server/reactive/HypermediaWebFilterTest.java +++ b/src/test/java/org/springframework/hateoas/server/reactive/HypermediaWebFilterTest.java @@ -15,13 +15,6 @@ */ package org.springframework.hateoas.server.reactive; -import static org.assertj.core.api.AssertionsForInterfaceTypes.*; -import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*; -import static org.springframework.hateoas.server.reactive.WebFluxLinkBuilder.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -32,12 +25,18 @@ import org.springframework.hateoas.Link; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.config.EnableHypermediaSupport; -import org.springframework.hateoas.config.WebClientConfigurer; +import org.springframework.hateoas.config.HypermediaWebTestClientConfigurer; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.config.EnableWebFlux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.*; +import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*; +import static org.springframework.hateoas.server.reactive.WebFluxLinkBuilder.*; /** * Verify WebFlux properly activates the {@link org.springframework.web.filter.reactive.ServerWebExchangeContextFilter}. @@ -55,10 +54,9 @@ class HypermediaWebFilterTest { ctx.register(WebFluxConfig.class); ctx.refresh(); - WebClientConfigurer webClientConfigurer = ctx.getBean(WebClientConfigurer.class); + HypermediaWebTestClientConfigurer configurer = ctx.getBean(HypermediaWebTestClientConfigurer.class); - this.testClient = WebTestClient.bindToApplicationContext(ctx).build().mutate() - .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()).build(); + this.testClient = WebTestClient.bindToApplicationContext(ctx).build().mutateWith(configurer); } /** @@ -67,7 +65,15 @@ class HypermediaWebFilterTest { @Test void webFilterShouldEmbedExchangeIntoContext() { - this.testClient.get().uri("https://example.com/api") // + String results = this.testClient.get().uri("https://example.com/api") // + .exchange() // + .expectStatus().isOk() // + .expectBody(String.class) // + .returnResult().getResponseBody(); + + System.out.println("results = " + results); + + this.testClient.get().uri("https://example.com/api") // .accept(MediaTypes.HAL_JSON) // .exchange() // .expectStatus().isOk() // @@ -81,7 +87,8 @@ class HypermediaWebFilterTest { .containsExactly(Link.of("https://example.com/api", IanaLinkRelations.SELF)); return true; - }).verifyComplete(); + }) // + .verifyComplete(); } @RestController diff --git a/src/test/java/org/springframework/hateoas/support/ContextTester.java b/src/test/java/org/springframework/hateoas/support/ContextTester.java index 1913d8e9..acf1027a 100644 --- a/src/test/java/org/springframework/hateoas/support/ContextTester.java +++ b/src/test/java/org/springframework/hateoas/support/ContextTester.java @@ -15,11 +15,11 @@ */ package org.springframework.hateoas.support; -import java.util.function.Function; - import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import java.util.function.Function; + /** * @author Greg Turnquist */