I tried different approaches that I found here on Stackoverflow. This is the way I know how to use a mapper with MapStruct.
I have a Mapper class like this:
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface DummyMapper {
DummyMapper INSTANCE = Mappers.getMapper(DummyMapper.class);
DummyResponseApi modelToApi(DummyResponse DummyResponseModel);
}
And my Unit test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {DummyMapper.class})
class ServiceClassTest {
private DummyService service;
}
My Service method that I want to test:
public DummyModelApi getSomething() {
DummyModel mapMe = new DummyModel();
return DummyMapper.INSTANCE.modelToApi(mapMe);
}
In older projects I did it the same way like this and had no problems. Now I'm using it in a new project with Spring Boot 2.5.6 and MapStruct 1.5.0.Beta1.
With using #SpringBootTest, as far as I know, Spring is actually starting the application and should create the Mapper class, so I don't understand, why the Mapper is always null?!
When I remove the DummyMapper.class in #SpringBootTest, an error appears with "Failed to load application context". That shows me, that the mapper is recognized.
Another thing that I find strange is, that I must use "unmappedTargetPolicy = ReportingPolicy.IGNORE" in my mapper, otherwise I get the error message "Unmapped properties could not found" or something, even though there is definetly the property with the same name in both models. This was always no problem in older projects, don't know why MapStruct is doing weird things now.
Omg, I found the solution. Now I know why I had to add "#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)" in my mapper.
Found the solution here: https://stackoverflow.com/a/69649688/8743351
Lombok needs to be before mapstruct in pom.xml/gradle.build.
Related
I am trying to add checker framework's Nullness checker to our project, however I am having problem with our MapStruct converters.
Converter example
Lets say I have a converter from User to UserDto like following:
#MapperConfig(componentModel = SPRING)
public interface UserToUserDtoConverter
extends org.springframework.core.convert.converter.Converter<User, UserDto> {
}
Which generates the following implementation:
#Override
public UserDto convert(User source) {
if ( source == null ) {
return null;
}
UserDtoBuilder<?, ?> userDto = UserDto.builder();
userDto.id(source.getId());
return userDto.build();
}
Problem
Now the problem is that Checker framework complains about the return null;, as the implementation does not have #Nullable above the convert method.
Another problem is when the converter uses other converters that are autowired here, which results in initialization.field.uninitialized error.
Things I have tried
Now I know that I could simply ignore converters completely with -AskipDefs, however I would still like it to let it check that there won't be a problem with assigning #Nullable value from User to #NonNull value in UserDto (and vice versa, which could leave a hole in the project).
Another solution that came to my mind was adding #SuppressWarning annotation for these error codes in the converter interface, however mapstruct is not capable of propagating any annotation to the implementation if I am not mistaken mapstruct-issues.
Stub files won't help here either.
Is here some kind of solution for handling the generated code?
I am trying to interface with another system that is has extremely specific integration parameters. They don't have any code written to ignore case sensitivity and, long story short, for a post request I am trying to make, they are expecting a JSON body with field names in Pascal case instead of Camel Case and the request fails without Pascal. We are using WebClient to send integration calls so we can support reactive flows in our code. As far as I've been able to tell, when I use WebClient to serialize to JSON, the request is being converted to use Camel Case, which I would normally want.
How can I serialize this to Pascal instead? Everything I try to research about this ends up landing me in .NET land, but I'm not writing this in C#. I'm writing it in Java.
//For example:
{"originTypeCode":"US","camelCaseFieldName":"FAILED"} // FAILURE
{"OriginTypeCode":"US","PascalFieldName":"SUCCESS"} // SUCCESS
I have two ideas:
1) This seems less ideal, but perhaps more intuitive. The idea is to convert the object I'm trying to post to JSON first, then with a parser convert all the fields from Camel Case to Pascal, then try and post that with my WebClient method. This doesn't seem like the most ideal way to do this. I'd imagine there is probably something a lot cleaner.
2) The second idea is that my WebClient instance serializes using a Jackson serializer. I think if I were to create a new Bean of WebClient/Jackson ObjectMapper, maybe I can write a custom converter to use specifically for this integration flow. This seems like it is perhaps cleaner, but digging through WebClient and it's build methods, it's difficult to figure out how to accomplish this. Below I'm posting the beans as I have them currently defined. Digging into this kind of thing is pretty new to me, so I'm not sure what would need to be changed or where. The WebClient bean is from a WebClientConfig class and the ObjectMapper is from my JacksonConfig class.
#Bean
public WebClient webClient() {
return WebClient.builder().clientConnector(getClientHttpConnector()).build();
}
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
I am definitely open to other suggestions as well.
The comments from #GriffeyDog have helped me figure out what I need to do. For anyone that stumbles across this, the solution was the following:
If you want certain classes to serialize in specific ways, you can annotate the class itself with the annotation #JsonNaming, and then specify a naming strategy, a list of which can be found here: https://java-focus.com/jackson-property-naming-strategy/. For my use case, I used PropertyNamingStrategy.UpperCamelCaseStrategy.
#JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public class MyPascalSerializedClass {}
//All fields in this class will serialize to "UpperCamelCase" instead of "normalCamelCase".
Additionally, if you wanted to specify certain fields, you can use the #JsonProperty annotation to override even the class annotation. For example, I had a field within my class that had to map to a JSON format that didn't fit to any standard convention, so I was able to use this.
#JsonProperty("ULDNumber")
private String uldNumber
//This field will serialize to the specified "ULDNumber".
This is all part of the com.fasterxml.jackson library. For further documentation you can refer to the link above and the following: https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/PropertyNamingStrategy.UpperCamelCaseStrategy.html
I am big on clean well-isolated unit tests. But I am stumbling on the "clean" part here for testings a controller that uses DomainClassConverter feature to get entities as parameters for its mapped methods.
#Entity
class MyEntity {
#Id
private Integer id;
// rest of properties goes here.
}
The controller is defined like this
#RequestMapping("/api/v1/myentities")
class MyEntitiesController {
#Autowired
private DoSomethingService aService;
#PostMapping("/{id}")
public ResponseEntity<MyEntity> update(#PathVariable("id")Optional<MyEntity> myEntity) {
// do what is needed here
}
}
So from the DomainClassConverter small documentation I know that it uses CrudRepository#findById to find entities. What I would like to know is how can I mock that cleanly in a test.
I have had some success by doing this steps:
Create a custom Converter/Formatter that I can mock
Instantiate my own MockMvc with above converter
reset mock and change behaviour at each test.
The problem is that the setup code is complex and thus hard to debug and explain (my team is 99% junior guys coming from rails or uni so we have to keep things simple). I was wondering if there is a way to inject the desired MyEntity instances from my unit test while keep on testing using the #Autowired MockMvc.
Currently I am trying to see if I can inject a mock of the CrudRepository for MyEntity but no success. I have not worked in Spring/Java in a few years (4) so my knowledge of the tools available might not be up to date.
So from the DomainClassConverter small documentation I know that it uses CrudRepository#findById to find entities. What I would like to know is how can I mock that cleanly in a test.
You will need to mock 2 methods that are called prior the CrudRepository#findById in order to return the entity you want. The example below is using RestAssuredMockMvc, but you can do the same thing with MockMvc if you inject the WebApplicationContext as well.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SomeApplication.class)
public class SomeControllerTest {
#Autowired
private WebApplicationContext context;
#MockBean(name = "mvcConversionService")
private WebConversionService webConversionService;
#Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
SomeEntity someEntity = new SomeEntity();
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
}
}
At some point Spring Boot will execute the WebConversionService::convert, which will later call DomainClassConverter::convert and then something like invoker.invokeFindById, which will use the entity repository to find the entity.
So why mock WebConversionService instead of DomainClassConverter? Because DomainClassConverter is instantiated during application startup without injection:
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<>(conversionService);
Meanwhile, WebConversionService is a bean which will allow us to mock it:
#Bean
#Override
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
It is important to name the mock bean as mvcConversionService, otherwise it won't replace the original bean.
Regarding the stubs, you will need to mock 2 methods. First you must tell that your mock can convert anything:
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
And then the main method, which will match the desired entity ID defined in the URL path:
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
So far so good. But wouldn't be better to match the destination type as well? Something like eq(TypeDescriptor.valueOf(SomeEntity.class))? It would, but this creates a new instance of a TypeDescriptor, which will not match when this stub is called during the domain conversion.
This was the cleanest solution I've put to work, but I know that it could be a lot better if Spring would allow it.
I have two ObjectMapper instances of Jackson. (Using v2.8.3)
The first one is instantiated as follows:
ObjectMapper objectMapper = new ObjectMapper();
The other one is autowired from the Spring Boot context via #Autowired.
Both have the same visibility configurations like this:
But they produce different serializations for the same object. The differences I realized are as follows:
Order of the serialized fields
Serialization of protected transient fields. (The autowired instance does not serialize them at all.)
Case differences in the serialized fields i.e. the former generates "monitoringUserID" while the latter "MonitoringUserID".
What I want is that the autowired mapper would also serialize the protected transient fields.
I'd also be glad if you tell me the reasons on the other differences in both serializations.
A simplified version of an example class being serialized:
public class ClauseType implements Serializable {
protected transient List<ClauseTypeNoteItem> noteItems;
public ClauseType() {
}
public List<ClauseTypeNoteItem> getNoteItems() {
...
}
public void setNoteItems(List<ClauseTypeNoteItem> value) {
...
}
}
Complete ClauseType class: https://pastebin.com/m3h1hesn
Complete ClauseTypeNoteItem class: https://pastebin.com/dmphNV4e
Edit:
I realized that both instances had difference mapper features. According to the docs, after enabling the DEFAULT_VIEW_INCLUSION featured of the autowired instance, they had the same mapper features represented as 1068991. However the differences are still there. I also realized that the autowired mapper has two registered modules: org.springframework.boot.jackson.JsonComponentModule and com.fasterxml.jackson.datatype.joda.JodaModule. I'm not sure whether the second module has an effect on the results I'm getting.
Thanks in advance.
It turned out that Spring injects a JaxbAnnotationIntrospector coming from jackson-module-jaxb-annotations maven dependency. During the instantiations of BeanDescriptions, the Jaxb introspector overrides the visibility checker settings that I provided. So, as a solution I will inject a new ObjectMapper bean and mark it as #Primary.
If you think of any other better solution, let me know
In Spring Boot the Jackson ObjectMapper is build and customised by Jackson2ObjectMapperBuilder object. The main source of configuration are the spring.jackson.* properties as explained in the docs:
74.3 Customize the Jackson ObjectMapper
Appendix A. Common application properties
Is it possible to use a system-property for the value of the #WebappConfiguration annotation? I have tried something like:
#WebAppConfiguration("#{systemProperties['webapproot'] ?: 'war'}")
But it doesn't seem to work at all. Is there any other way to do this through spring? I don't want to do this through our build tools as it would break executing our integration tests from our IDEs.
#WebAppConfiguration doesn't seems to support neither SpEL nor placeholder resolving.
I checked it in the following way: I tried to inject system property and resolve placeholder using #Value. When I tried to inject a nonexistent property #Value failed throwing SpelEvaluationException: EL1008E: Property or field 'asd' cannot be found on object of type 'java.util.Properties' and IllegalArgumentException: Could not resolve placeholder 'nonexistent_property' in value "${nonexistent_property}" respectively while #WebAppConfiguration's value had simply #{systemProperties.asd} and ${nonexistent_property} intialized as simple String's.
No, that is unfortunately not supported.
The value supplied to #WebAppConfiguration must be an an explicit resource path as stated in the class-level JavaDoc.
If you would like for us to consider dynamic evaluation of the value attribute, please feel free to open a JIRA issue to request such support.
Regards,
Sam (author of the Spring TestContext Framework)
I have found a solution to this problem. I wrote my own ContextBootsTraper by extending WebTestContextBootstrapper which Spring uses to load the WebAppConfiguration-Annotation value.
I extended the functionality to include a check if a certain system-property exists and overwrite the annotations value if it does:
public class SystemPropTestContextBootstrapper extends WebTestContextBootstrapper {
#Override
protected MergedContextConfiguration processMergedContextConfiguration(MergedContextConfiguration mergedConfig) {
WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(mergedConfig.getTestClass(),
WebAppConfiguration.class);
if (webAppConfiguration != null) {
//implementation ommited
String webappDir = loadWebappDirFromSystemProperty();
if(webappDir == null) {
webappDir = webAppConfiguration.value();
}
return new WebMergedContextConfiguration(mergedConfig, webappDir);
}
return mergedConfig;
}
}
This class can then be used with the #BootstrapWith-Annotation:
#RunWith(SpringJUnit4ClassRunner.class)
#BootstrapWith(SystemPropTestContextBootstrapper.class)
#WebAppConfiguration("standardDir")
public class SomeTest {
}
This solution enables me to run the tests from my build-tool while maintaining the ability to run the tests from my IDE, which is great.