Fix GH-127 add support for HATEOAS (#140)
* Fix gh-127, HATEOAS support * Fix gh-127, HATEOAS support tests * Add DefaultCurieProvider for HAL Jackson http converter
This commit is contained in:
committed by
Ryan Baxter
parent
f76a81fe6b
commit
2e8a5833a9
@@ -482,7 +482,7 @@ The following feign client uses the `Params` class by using the `@SpringQueryMap
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@FeignClient("demo")
|
||||
public class DemoTemplate {
|
||||
public interface DemoTemplate {
|
||||
|
||||
@GetMapping(path = "/demo")
|
||||
String demoEndpoint(@SpringQueryMap Params params);
|
||||
@@ -490,3 +490,23 @@ public class DemoTemplate {
|
||||
----
|
||||
|
||||
If you need more control over the generated query parameter map, you can implement a custom `QueryMapEncoder` bean.
|
||||
|
||||
=== HATEOAS support
|
||||
|
||||
Spring provides some APIs to create REST representations that follow the https://en.wikipedia.org/wiki/HATEOAS[HATEOAS] principle, https://spring.io/projects/spring-hateoas[Spring Hateoas] and https://spring.io/projects/spring-data-rest[Spring Data REST].
|
||||
|
||||
If your project use the `org.springframework.boot:spring-boot-starter-hateoas` starter
|
||||
or the `org.springframework.boot:spring-boot-starter-data-rest` starter, Feign HATEOAS support is enabled by default.
|
||||
|
||||
When HATEOAS support is enabled, Feign clients are allowed to serialize
|
||||
and deserialize HATEOAS representation models: https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/EntityModel.html[EntityModel], https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/CollectionModel.html[CollectionModel] and https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/PagedModel.html[PagedModel].
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@FeignClient("demo")
|
||||
public interface DemoTemplate {
|
||||
|
||||
@GetMapping(path = "/stores")
|
||||
CollectionModel<Store> getStores();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
<artifactId>spring-boot-starter-reactor-netty</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-hateoas</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
@@ -208,6 +213,11 @@
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.protobuf-java-format</groupId>
|
||||
<artifactId>protobuf-java-format</artifactId>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.DefaultCurieProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
|
||||
import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
||||
import static org.springframework.hateoas.MediaTypes.HAL_JSON_UTF8;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(RepresentationModel.class)
|
||||
@AutoConfigureAfter({ JacksonAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class,
|
||||
RepositoryRestMvcAutoConfiguration.class })
|
||||
@AutoConfigureBefore(HypermediaAutoConfiguration.class)
|
||||
public class FeignHalAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter(
|
||||
ObjectProvider<ObjectMapper> objectMapper,
|
||||
ObjectProvider<HalConfiguration> halConfiguration,
|
||||
ObjectProvider<LinkRelationProvider> relProvider,
|
||||
ObjectProvider<CurieProvider> curieProvider,
|
||||
ObjectProvider<MessageSourceAccessor> linkRelationMessageSource) {
|
||||
|
||||
ObjectMapper mapper = objectMapper.getIfAvailable(ObjectMapper::new).copy();
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
|
||||
HalConfiguration configuration = halConfiguration
|
||||
.getIfAvailable(HalConfiguration::new);
|
||||
|
||||
CurieProvider curieProviderInstance = curieProvider
|
||||
.getIfAvailable(() -> new DefaultCurieProvider(Collections.emptyMap()));
|
||||
|
||||
Jackson2HalModule.HalHandlerInstantiator halHandlerInstantiator = new Jackson2HalModule.HalHandlerInstantiator(
|
||||
relProvider.getObject(), curieProviderInstance,
|
||||
linkRelationMessageSource.getObject(), configuration);
|
||||
|
||||
mapper.setHandlerInstantiator(halHandlerInstantiator);
|
||||
|
||||
if (!Jackson2HalModule.isAlreadyRegisteredIn(mapper)) {
|
||||
Jackson2HalModule halModule = new Jackson2HalModule();
|
||||
mapper.registerModule(halModule);
|
||||
}
|
||||
|
||||
TypeConstrainedMappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
|
||||
RepresentationModel.class);
|
||||
converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON, HAL_JSON_UTF8));
|
||||
converter.setObjectMapper(mapper);
|
||||
return converter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
|
||||
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
|
||||
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
|
||||
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
|
||||
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
@@ -86,7 +87,8 @@ public class FeignPageableEncodingTests {
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app",
|
||||
exclude = { RepositoryRestMvcAutoConfiguration.class })
|
||||
@EnableSpringDataWebSupport
|
||||
@Import({ NoSecurityConfiguration.class, FeignClientsConfiguration.class })
|
||||
public static class Application {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
public class FeignHalAutoConfigurationContextTests {
|
||||
|
||||
private WebApplicationContextRunner contextRunner;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
contextRunner = new WebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class,
|
||||
HypermediaAutoConfiguration.class,
|
||||
RepositoryRestMvcAutoConfiguration.class,
|
||||
FeignHalAutoConfiguration.class))
|
||||
.withPropertyValues("debug=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHalJacksonHttpMessageConverterIsNotLoaded() {
|
||||
FilteredClassLoader filteredClassLoader = new FilteredClassLoader(
|
||||
RepositoryRestMvcConfiguration.class, RepresentationModel.class);
|
||||
contextRunner.withClassLoader(filteredClassLoader)
|
||||
.run(context -> assertThat(context)
|
||||
.doesNotHaveBean("halJacksonHttpMessageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHalJacksonHttpMessageConverterIsLoaded() {
|
||||
FilteredClassLoader filteredClassLoader = new FilteredClassLoader(
|
||||
RepositoryRestMvcConfiguration.class);
|
||||
contextRunner.withClassLoader(filteredClassLoader).run(
|
||||
context -> assertThat(context).hasBean("halJacksonHttpMessageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHalJacksonHttpMessageConverterIsNotLoadedUseRestDataMessageConverterInstead() {
|
||||
contextRunner.run(
|
||||
context -> assertThat(context).hasBean("halJacksonHttpMessageConverter"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
||||
import static org.springframework.hateoas.MediaTypes.HAL_JSON_UTF8;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FeignHalAutoConfigurationTests {
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<ObjectMapper> objectMapper;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<HalConfiguration> halConfiguration;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<LinkRelationProvider> relProvider;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<CurieProvider> curieProvider;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<MessageSourceAccessor> linkRelationMessageSource;
|
||||
|
||||
@InjectMocks
|
||||
private FeignHalAutoConfiguration feignHalAutoConfiguration;
|
||||
|
||||
@Test
|
||||
public void halJacksonHttpMessageConverter() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
when(objectMapper.getIfAvailable(any())).thenReturn(mapper);
|
||||
|
||||
when(halConfiguration.getIfAvailable(any()))
|
||||
.thenReturn(mock(HalConfiguration.class));
|
||||
when(relProvider.getObject()).thenReturn(mock(LinkRelationProvider.class));
|
||||
when(curieProvider.getIfAvailable(any())).thenReturn(mock(CurieProvider.class));
|
||||
when(linkRelationMessageSource.getObject())
|
||||
.thenReturn(mock(MessageSourceAccessor.class));
|
||||
|
||||
TypeConstrainedMappingJackson2HttpMessageConverter converter = feignHalAutoConfiguration
|
||||
.halJacksonHttpMessageConverter(objectMapper, halConfiguration,
|
||||
relProvider, curieProvider, linkRelationMessageSource);
|
||||
|
||||
assertThat(converter).isNotNull();
|
||||
assertThat(converter.getObjectMapper()).isNotNull();
|
||||
assertThat(converter.getSupportedMediaTypes())
|
||||
.isEqualTo(Arrays.asList(HAL_JSON, HAL_JSON_UTF8));
|
||||
|
||||
assertThat(Jackson2HalModule.isAlreadyRegisteredIn(converter.getObjectMapper()))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.openfeign.hateoas.app.FeignHalApplication;
|
||||
import org.springframework.cloud.openfeign.hateoas.app.FeignHalClient;
|
||||
import org.springframework.cloud.openfeign.hateoas.app.MarsRover;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedModel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* Test HATEOAS support.
|
||||
*
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@SpringBootTest(classes = FeignHalApplication.class, webEnvironment = RANDOM_PORT,
|
||||
value = "debug=true")
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class FeignHalTests {
|
||||
|
||||
@Autowired
|
||||
private FeignHalClient feignHalClient;
|
||||
|
||||
@Test
|
||||
public void testEntityModel() {
|
||||
EntityModel<MarsRover> entity = feignHalClient.entity();
|
||||
assertThat(entity).isNotNull();
|
||||
|
||||
assertThat(entity.hasLinks()).isTrue();
|
||||
assertThat(entity.hasLink("self")).isTrue();
|
||||
|
||||
assertThat(entity.getLink("self")).map(Link::getHref).contains("/entity");
|
||||
|
||||
MarsRover marsRover = entity.getContent();
|
||||
assertThat(marsRover).isNotNull();
|
||||
assertThat(marsRover.getName()).isEqualTo("Sojourner");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionModel() {
|
||||
CollectionModel<MarsRover> collectionModel = feignHalClient.collection();
|
||||
assertThat(collectionModel).isNotNull();
|
||||
assertThat(collectionModel).isNotEmpty();
|
||||
|
||||
assertThat(collectionModel.hasLinks()).isTrue();
|
||||
assertThat(collectionModel.hasLink("self")).isTrue();
|
||||
|
||||
assertThat(collectionModel.getLink("self")).map(Link::getHref)
|
||||
.contains("/collection");
|
||||
|
||||
Collection<MarsRover> collection = collectionModel.getContent();
|
||||
assertThat(collection).isNotEmpty();
|
||||
|
||||
MarsRover marsRover = collection.stream().findAny().orElse(null);
|
||||
assertThat(marsRover).isNotNull();
|
||||
assertThat(marsRover.getName()).isEqualTo("Opportunity");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPagedModel() {
|
||||
PagedModel<MarsRover> paged = feignHalClient.paged();
|
||||
assertThat(paged).isNotNull();
|
||||
assertThat(paged).isNotEmpty();
|
||||
|
||||
assertThat(paged.hasLinks()).isTrue();
|
||||
assertThat(paged.hasLink("self")).isTrue();
|
||||
|
||||
assertThat(paged.getLink("self")).map(Link::getHref).contains("/paged");
|
||||
|
||||
Collection<MarsRover> collection = paged.getContent();
|
||||
assertThat(collection).isNotEmpty();
|
||||
|
||||
MarsRover marsRover = collection.stream().findAny().orElse(null);
|
||||
assertThat(marsRover).isNotNull();
|
||||
assertThat(marsRover.getName()).isEqualTo("Curiosity");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas.app;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Test HATEOAS application.
|
||||
*
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@EnableFeignClients(clients = FeignHalClient.class)
|
||||
@RibbonClient(name = "local", configuration = FeignHalRibbonConfiguration.class)
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.hateoas.app",
|
||||
exclude = RepositoryRestMvcAutoConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public class FeignHalApplication {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas.app;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.PagedModel;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@FeignClient("local")
|
||||
public interface FeignHalClient {
|
||||
|
||||
@GetMapping("entity")
|
||||
EntityModel<MarsRover> entity();
|
||||
|
||||
@GetMapping("collection")
|
||||
CollectionModel<MarsRover> collection();
|
||||
|
||||
@GetMapping("paged")
|
||||
PagedModel<MarsRover> paged();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas.app;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedModel;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@RestController
|
||||
public class FeignHalController {
|
||||
|
||||
@GetMapping("/entity")
|
||||
public EntityModel<MarsRover> getEntity() {
|
||||
MarsRover marsRover = new MarsRover();
|
||||
marsRover.setName("Sojourner");
|
||||
Link link = new Link("/entity", "self");
|
||||
return new EntityModel<>(marsRover, link);
|
||||
}
|
||||
|
||||
@GetMapping("/collection")
|
||||
public CollectionModel<MarsRover> getCollection() {
|
||||
MarsRover marsRover = new MarsRover();
|
||||
marsRover.setName("Opportunity");
|
||||
Link link = new Link("/collection", "self");
|
||||
return new CollectionModel<>(Collections.singleton(marsRover), link);
|
||||
}
|
||||
|
||||
@GetMapping("/paged")
|
||||
public CollectionModel<MarsRover> getPaged() {
|
||||
MarsRover marsRover = new MarsRover();
|
||||
marsRover.setName("Curiosity");
|
||||
Link link = new Link("/paged", "self");
|
||||
PagedModel.PageMetadata metadata = new PagedModel.PageMetadata(1, 1, 1);
|
||||
return new PagedModel<>(Collections.singleton(marsRover), metadata, link);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas.app;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
public class FeignHalRibbonConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int serverPort = 0;
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer() {
|
||||
Server server = new Server("localhost", serverPort);
|
||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
||||
balancer.setServersList(Collections.singletonList(server));
|
||||
return balancer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.cloud.openfeign.hateoas.app;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
*/
|
||||
public class MarsRover {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user