how to use RestMediaTypes.SCHEMA_JSON in Spring Data Rest - spring-data-rest

Can someone let me know how to use RestMediaTypes.SCHEMA_JSON?
This is my current controller annotation:
#Controller
#ExposesResourceFor(Order.class)
#RequestMapping(value = "/orders", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderController {
}
I have added the following in pom.xml
spring-data-rest-webmvc

Related

How can I mock the service class in my Controller Test in Micronaut using JUnit5?

I am writing a JUnit test case for the controller in my micronaut application. The controller has a GET endpoint which invokes a method in my service class. I am getting a NullPointerException so I am assuming that my service class might not be properly mocked however, I am not sure. I am using #Mock (Mockito) for the service.
Am I using the correct annotation to mock the service layer? I have tried to search on google but it hasn't given me much to look into. Thanks.
#MicronautTest
public class FPlanControllerTest {
private static final String url = "dummy_url";
#Inject
FPlanService fplanService;
#Inject
#Client("/")
RxHttpClient client;
#Test
public void testGetLayout() {
FPlanUrl expectedFPlanUrl = new FPlanUrl(url);
when(fplanService.getLayoutUrl(Mockito.anyString(), Mockito.anyString()))
.thenReturn(expectedFPlanUrl);
FPlanUrl actualFPlanUrl = client.toBlocking()
.retrieve(HttpRequest.GET("/layout/1000545").header("layoutId", "7"), FPlanUrl.class);
assertEquals(expectedFPlanUrl , actualFPlanUrl);
}
#MockBean(FPlanService.class)
FPlanService fplanService() {
return mock(FPlanService.class);
}
}
I received the below error.
java.lang.NullPointerException at com.apartment.controller.FPlanControllerTest.testGetLayout(FPlanControllerTest.java:44)
Use #MockBean (io.micronaut.test.annotation.MockBean).
Docs - https://micronaut-projects.github.io/micronaut-test/latest/guide/#junit5
Simply try to mock as below :-
#MockBean(MyService.class)
MyService myService() {
return mock(MyService.class);
}
Now the service can be injected as:-
#Inject
private MyService myService;
Use inside your test method as:-
#Test
public void myServiceTest() {
when(myService.foo(any())).thenReturn(any());
MutableHttpResponse<FooResponse> response = controller.bar(new
MyRequest());
Assertions.assertNotNull(response);
}
I figured out what went wrong. This was giving a NullPointerException because the HTTP response was expecting a String and not the FPlanUrl object. The correct code is as below:
#Test
public void testGetLayout() {
FPlanUrl expectedFPlanUrl = new FPlanUrl("http://dummyurl.com");
when(fplanService.getLayoutUrl(Mockito.anyString(), Mockito.anyString()))
.thenReturn(expectedFPlanUrl);
Assertions.assertEquals("{\"url\":\"http://dummyurl.com\"}", client.toBlocking().retrieve(HttpRequest.GET("/layout/123").header("layoutId", "7"), String.class);
verify(fplanService).getLayoutUrl("123","7");
}

Jackson - mapping OffsetDateTime [duplicate]

I have problems LocalDateTime deserialization in Junit test. I have simple REST API which returns some DTO object. When I call my endpoint there is no problem with response - it is correct. Then I try to write unit test, obtain MvcResult and with use of ObjectMapper convert it to my DTO object. But I still receive:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.time.LocalDateTime` out of START_ARRAY token
at [Source: (String)"{"name":"Test name","firstDate":[2019,3,11,18,34,43,52217600],"secondDate":[2019,3,11,19,34,43,54219000]}"; line: 1, column: 33] (through reference chain: com.mylocaldatetimeexample.MyDto["firstDate"])
I was trying with #JsonFormat and adding compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.9.8' to my build.gradle but I use Spring Boot 2.1.3.RELEASE so it is involved in it. I do not have any idea how to fix it. My simple endpoint and unit test below:
#RestController
#RequestMapping("/api/myexample")
public class MyController {
#GetMapping("{id}")
public ResponseEntity<MyDto> findById(#PathVariable Long id) {
MyDto myDto = new MyDto("Test name", LocalDateTime.now(), LocalDateTime.now().plusHours(1));
return ResponseEntity.ok(myDto);
}
}
MyDto class
public class MyDto {
private String name;
private LocalDateTime firstDate;
private LocalDateTime secondDate;
// constructors, getters, setters
}
Unit test
public class MyControllerTest {
#Test
public void getMethod() throws Exception {
MyController controller = new MyController();
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/api/myexample/1"))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
String json = mvcResult.getResponse().getContentAsString();
MyDto dto = new ObjectMapper().readValue(json, MyDto.class);
assertEquals("name", dto.getName());
}
}
You create new ObjectMapper in test class:
MyDto dto = new ObjectMapper().readValue(json, MyDto.class);
Try to inject ObjectMapper from Spring context or manually register module:
mapper.registerModule(new JavaTimeModule());
See also:
jackson-modules-java8

Resteasy and Google Guice: how to use multiple #ApplicationPath and resource with #Injection?

I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.
My intentions are:
Use multiple #ApplicationPath for the versions of my API. In each class annotated with #ApplicationPath I load a set of classes for the specific version.
Each resource have a #Inject (from Google Guice) in his constructor to inject some services.
I created two classes annotated with #ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.
My Module is configured like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:
java.lang.RuntimeException: RESTEASY003190: Could not find constructor
for class: org.jboss.resteasy.examples.guice.hello.HelloResource
Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.
But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.
If I abandon the idea of use #ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(HelloResource.class);
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(UserResource.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple #ApplicationPath.
So, what I'm missing or not understanding?
I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md
Thanks!
When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection.
And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.
Below are the changes that I made to make it work the way you want.
ApplicationV1RS.java
#ApplicationPath("v1")
public class ApplicationV1RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV1RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
ApplicationV2RS.java
#ApplicationPath("v2")
public class ApplicationV2RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV2RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
HelloResource.java
#Path("hello")
public class HelloResource {
#Inject
private IGreeterService greeter;
public HelloResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return greeter.greet(name);
}
}
UserResource.java
#Path("user")
public class UserResource {
#Inject
private IUserService userService;
public UserResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return userService.getUser(name);
}
}
Add #Singleton to your Service Classes.
Hope it helps.
I have also pushed the code to forked repo. check it out

Replace #Value property within #Configuration during Spring Boot test

Scenario
I've got a Spring Boot application with a #Configuration annotated Spring configuration class which contains some #Value annotated fields. For testing I want to replace these field values with custom test values.
Unfortunately these test values cannot be overridden using a simple properties file, (String) constants or similar, instead I must use some custom written property resolving Java class (e.g. TargetProperties.getProperty("some.username")).
The problem I have is that when I add a custom PropertySource to the ConfigurableEnvironment within my test configuration, it's already too late because this PropertySource will be added after the e.g. RestTemplate has been created.
Question
How can I override #Value annotated fields within a #Configuration class with properties obtained programmatically via custom Java code before anything else gets initialized?
Code
Production Configuration Class
#Configuration
public class SomeConfiguration {
#Value("${some.username}")
private String someUsername;
#Value("${some.password}")
private String somePassword;
#Bean
public RestTemplate someRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(
new BasicAuthorizationInterceptor(someUsername, somePassword));
return restTemplate;
}
}
Test Configuration Class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {
#SpringBootConfiguration
#Import({MySpringBootApp.class, SomeConfiguration.class})
static class TestConfiguration {
#Autowired
private ConfigurableEnvironment configurableEnvironment;
// This doesn't work:
#Bean
#Lazy(false)
// I also tried a #PostConstruct method
public TargetPropertiesPropertySource targetPropertiesPropertySource() {
TargetPropertiesPropertySource customPropertySource =
new TargetPropertiesPropertySource();
configurableEnvironment.getPropertySources().addFirst(customPropertySource);
return customPropertySource;
}
}
}
You can override properties directly in the #SpringBootTest annotation using the properties parameter:
#SpringBootTest(properties = {"some.username=user", "some.password=pwd"},
webEnvironment = SpringBootTest.WebEnvironment.NONE)
You can use #TestPropertySource
#TestPropertySource(
properties = {
"some.username=validate",
"some.password=false"
}
)
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
//...
}
You can use constructor injection in production cases, which allows it to set the configuration manually:
#Configuration
public class SomeConfiguration {
private final String someUsername;
private final String somePassword;
#Autowired
public SomeConfiguration(#Value("${some.username}") String someUsername,
#Value("${some.password}") String somePassword) {
this.someUsername = someUsername;
this.somePassword = somePassword;
}
...
)
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {
private SomeConfiguration config;
#Before
public init() {
config = new SomeConfiguration("foo", "bar");
}
}

How to add links to root resource in Spring Data REST?

How to expose an external resource (not managed through a repository) in the root listing of resources of Spring Data REST? I defined a controller following the pattern in Restbucks
This can be done by implementing ResourceProcessor<RepositoryLinksResource>.
Following code snippet adds "/others" to the root listing
#Controller
#ExposesResourceFor(Other.class)
#RequestMapping("/others")
public class CustomRootController implements
ResourceProcessor<RepositoryLinksResource> {
#ResponseBody
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Resources<Resource<Other>>> listEntities(
Pageable pageable) throws ResourceNotFoundException {
//... do what needs to be done
}
#Override
public RepositoryLinksResource process(RepositoryLinksResource resource) {
resource.add(ControllerLinkBuilder.linkTo(CustomRootController.class).withRel("others"));
return resource;
}
}
should add
{
"rel": "others",
"href": "http://localhost:8080/api/others"
}
to your root listing links
I have been searching for an answer to the same issue, but the key is: I don't have a controller. My url points to something created in an auth filter. What worked for me is to create a RootController that doesn't have any methods, and use it for building links in the ResourceProcessor implementation.
#RestController
#RequestMapping("/")
public class RootController {}
Then the link is inserted using the empty controller.
#Component
public class AuthLinkProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource resource) {
resource.add(
linkTo(RootController.class)
.slash("auth/login")
.withRel("auth-login"));
return resource;
}
}
In 2022, API has changed. This reply might be relevant: Migrating ResourceProcessor to HATEOAS 1.0.0 M1.
Here's my piece of code with the new API:
#Component
class AuthLinkProcessor implements RepresentationModelProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource model) {
model.add(
linkTo(AuthenticationController.class)
.slash("/authenticate")
.withRel("authenticate"));
return model;
}
}