Skip to content

Commit

Permalink
inherit the default behaviour of spring for internal object mapper (#…
Browse files Browse the repository at this point in the history
…8051)

* inherit the default behaviour of spring for internal object mapper

* fix test

* add tests

* rest endpoint test refactor

* revert VaadinConnectTypeConversionEndpoints.java

* Remove the IT suffix from the test

* method renaming

* remove leftover code

* code formatting

* code format

* code formatting
  • Loading branch information
haijian-vaadin committed Apr 16, 2020
1 parent 03b4a1d commit 3c089c6
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,23 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand Down Expand Up @@ -126,12 +131,15 @@ public class VaadinConnectController {
* from
*/
public VaadinConnectController(
@Qualifier(VAADIN_ENDPOINT_MAPPER_BEAN_QUALIFIER) ObjectMapper vaadinEndpointMapper,
@Autowired(required = false) @Qualifier(VAADIN_ENDPOINT_MAPPER_BEAN_QUALIFIER)
ObjectMapper vaadinEndpointMapper,
VaadinConnectAccessChecker accessChecker,
EndpointNameChecker endpointNameChecker,
ExplicitNullableTypeChecker explicitNullableTypeChecker,
ApplicationContext context) {
this.vaadinEndpointMapper = vaadinEndpointMapper;
this.vaadinEndpointMapper = vaadinEndpointMapper != null
? vaadinEndpointMapper
: createVaadinConnectObjectMapper(context);
this.accessChecker = accessChecker;
this.explicitNullableTypeChecker = explicitNullableTypeChecker;

Expand All @@ -140,6 +148,17 @@ public VaadinConnectController(
name, endpointBean));
}

private ObjectMapper createVaadinConnectObjectMapper(ApplicationContext context) {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
JacksonProperties jacksonProperties = context
.getBean(JacksonProperties.class);
if (jacksonProperties.getVisibility().isEmpty()) {
objectMapper.setVisibility(PropertyAccessor.ALL,
JsonAutoDetect.Visibility.ANY);
}
return objectMapper;
}

private static Logger getLogger() {
return LoggerFactory.getLogger(VaadinConnectController.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@

import java.lang.reflect.Method;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
Expand All @@ -33,8 +27,6 @@

import com.vaadin.flow.server.connect.auth.VaadinConnectAccessChecker;

import static com.vaadin.flow.server.connect.VaadinConnectController.VAADIN_ENDPOINT_MAPPER_BEAN_QUALIFIER;

/**
* A configuration class for customizing the {@link VaadinConnectController}
* class.
Expand Down Expand Up @@ -142,24 +134,4 @@ public VaadinConnectAccessChecker accessChecker() {
public ExplicitNullableTypeChecker typeChecker() {
return new ExplicitNullableTypeChecker();
}

/**
* Registers a {@link ObjectMapper} bean instance.
*
* @param context
* Spring application context
* @return the object mapper for endpoint.
*/
@Bean
@Qualifier(VAADIN_ENDPOINT_MAPPER_BEAN_QUALIFIER)
public ObjectMapper vaadinEndpointMapper(ApplicationContext context) {
ObjectMapper objectMapper = new ObjectMapper();
JacksonProperties jacksonProperties = context
.getBean(JacksonProperties.class);
if (jacksonProperties.getVisibility().isEmpty()) {
objectMapper.setVisibility(PropertyAccessor.ALL,
JsonAutoDetect.Visibility.ANY);
}
return objectMapper;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -804,9 +804,34 @@ public void should_Never_UseSpringObjectMapper() {
mock(ExplicitNullableTypeChecker.class), contextMock);

verify(contextMock, never()).getBean(ObjectMapper.class);
verify(contextMock, times(1)).getBean(JacksonProperties.class);
verify(mockSpringObjectMapper, never()).setVisibility(
PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
verify(contextMock, never()).getBean(JacksonProperties.class);
}

@Test
public void should_NotOverrideVisibility_When_JacksonPropertiesProvideVisibility() {
ApplicationContext contextMock = mock(ApplicationContext.class);
ObjectMapper mockDefaultObjectMapper = mock(ObjectMapper.class);
JacksonProperties mockJacksonProperties = mock(JacksonProperties.class);
when(contextMock.getBean(ObjectMapper.class))
.thenReturn(mockDefaultObjectMapper);
when(contextMock.getBean(JacksonProperties.class))
.thenReturn(mockJacksonProperties);
when(mockJacksonProperties.getVisibility())
.thenReturn(Collections.singletonMap(PropertyAccessor.ALL,
JsonAutoDetect.Visibility.PUBLIC_ONLY));
new VaadinConnectController(null,
mock(VaadinConnectAccessChecker.class),
mock(EndpointNameChecker.class),
mock(ExplicitNullableTypeChecker.class),
contextMock,
mock(ServletContext.class));

verify(contextMock, never()).getBean(ObjectMapper.class);
verify(mockDefaultObjectMapper, never()).setVisibility(
PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
verify(contextMock, times(1)).getBean(JacksonProperties.class);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2020 Vaadin Ltd.
*
* 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
*
* http://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 com.vaadin.flow.server.connect.rest;

public class BeanWithPrivateFields {
@SuppressWarnings("unused")
private String codeNumber = "007";
private String name = "Bond";
private String firstName = "James";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

protected String getFirstName() {
return firstName;
}

protected void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright 2000-2020 Vaadin Ltd.
*
* 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
*
* http://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 com.vaadin.flow.server.connect.rest;

import javax.servlet.ServletContext;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import com.vaadin.flow.server.connect.EndpointNameChecker;
import com.vaadin.flow.server.connect.ExplicitNullableTypeChecker;
import com.vaadin.flow.server.connect.VaadinConnectController;
import com.vaadin.flow.server.connect.auth.VaadinConnectAccessChecker;

import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@WebMvcTest
@Import({VaadinConnectEndpoints.class, MyRestController.class})
public class EndpointWithRestControllerTest {

private MockMvc mockMvcForEndpoint;

@Autowired
private MockMvc mockMvcForRest;


@Autowired
private ApplicationContext applicationContext;

@Before
public void setUp() {
mockMvcForEndpoint = MockMvcBuilders.standaloneSetup(new VaadinConnectController(
null, mock(VaadinConnectAccessChecker.class),
mock(EndpointNameChecker.class),
mock(ExplicitNullableTypeChecker.class),
applicationContext,
mock(ServletContext.class)))
.build();
Assert.assertNotEquals(null, applicationContext);
}

@Test
//https://github.com/vaadin/flow/issues/8010
public void shouldNotExposePrivateAndProtectedFields_when_CallingFromRestAPIs()
throws Exception {
String result = mockMvcForRest.perform(get("/api/get")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
Assert.assertEquals("{\"name\":\"Bond\"}", result);
}

@Test
//https://github.com/vaadin/flow/issues/8034
public void should_BeAbleToSerializePrivateFieldsOfABean_when_CallingFromConnectEndPoint() {
try {
String result = callEndpointMethod("getBeanWithPrivateFields");
Assert.assertEquals("{\"codeNumber\":\"007\",\"name\":\"Bond\",\"firstName\":\"James\"}", result);
} catch (Exception e) {
fail("failed to serialize a bean with private fields");
}
}

@Test
//https://github.com/vaadin/flow/issues/8034
public void should_BeAbleToSerializeABeanWithZonedDateTimeField() {
try {
String result = callEndpointMethod("getBeanWithZonedDateTimeField");
assertNotNull(result);
assertNotEquals("", result);
assertNotEquals("{\"message\":\"Failed to serialize endpoint 'VaadinConnectTypeConversionEndpoints' method 'getBeanWithZonedDateTimeField' response. Double check method's return type or specify a custom mapper bean with qualifier 'vaadinEndpointMapper'\"}", result);
} catch (Exception e) {
fail("failed to serialize a bean with ZonedDateTime field");
}
}

private String callEndpointMethod(String methodName) throws Exception {
String endpointName = VaadinConnectEndpoints.class.getSimpleName();
String requestUrl = String.format("/%s/%s", endpointName, methodName);
RequestBuilder requestBuilder = MockMvcRequestBuilders.post(requestUrl)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

return mockMvcForEndpoint.perform(requestBuilder).andReturn().getResponse().getContentAsString();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2020 Vaadin Ltd.
*
* 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
*
* http://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 com.vaadin.flow.server.connect.rest;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MixedRestAndEndpointApplication {
// Test application to provide test context for the integration tests
}
Loading

0 comments on commit 3c089c6

Please sign in to comment.