I want to map UserDTO and UserGroupDTO where User has address as a list in which it has all address fields and Usergroup has individual address fields. Please let me know how could I map these fields.
There is currently no official support for that, but there is a workaround using expressions, as described in the ticket : https://github.com/mapstruct/mapstruct/issues/1321#issuecomment-339807380
This would work your case:
#Mapper
public abstract class UserDTOMapper {
#Mapping( expression = "java(userDTO.getAddress().get(0))", target = "street")
#Mapping( expression = "java(userDTO.getAddress().get(1))", target = "zipCode")
#Mapping( expression = "java(userDTO.getAddress().get(2))", target = "country")
abstract public UserGroupDTO mapTo(UserDTO userDTO);
}
But you have to make sure that the address property implemented as List will always contain the same number of fields and in correct order, else the mapping based on list index will not work as expected.
Related
I have a class with some fields:
class User {
String name;
String id;
// other fields
}
I'm getting a user from the service and want to assert that name and id fields are not empty. The object contains other fields, but those are not needed for this particular case. I tried to extract required fields and apply the allSatisfy {} function:
User user = service.getUser();
assertThat(user)
.extracting("name", "id") // extract only those fields that I need for now
.allSatisy { field -> assertThat(field).isNotEmpty() } // this doesn't compile because `field` is not a String
Am I using the extracting() function wrong?
Or is there other way of asserting multiple fields of an object at once instead of asserting each field separately?
Joop Eggen suggestion is a good solution, extracting multiple values at once returns the list of extract values wrapped in a ListAssert object so that you can (List) chain assertions. The list is a list of object because there is no way to know what type the list will contain (id could be an Integer and name a String so you can't assume you will be given a list of string here).
You can try asIntanceOf to narrow the type as you know more than AssertJ in this case, ex:
assertThat(user)
.extracting("name", "id")
.asInstanceOf(InstanceOfAssertFactories.list(String.class))
.allSatisy(field -> assertThat(field).isNotEmpty());
Whether it is better than casting the field, I'm not completely sure, I leave it to you to decide.
- Edit -
I tried your example and it worked fine with assertj core 3.21.0, this test is passing:
#Test
void test() {
User user = new User();
user.name = "Joe";
user.id = "123";
assertThat(user).extracting("name", "id")
.asInstanceOf(InstanceOfAssertFactories.list(String.class))
.allSatisfy(field -> assertThat(field).isNotEmpty());
}
So I guess this has to do with Kotlin.
Given a set of five objects:
KeyDto{String id}
ValueDto{String name, String value, String description}
Key{String id, String name}
Value{String value, String description}
Target{Key key, Value value}
I would like to create a mapper with two parameters:
Target dtosToTarget(KeyDto keyDto, ValueDto valueDto);
However, just defining helper methods for Key and Value seems not to be enough:
#Mapping(source = "keyDto.id", target = "id")
#Mapping(source = "valueDto.name", target = "name")
Key keyDtoAndValueDtoToKey(KeyDto keyDto, ValueDto valueDto);
Value valueDtoToValue(ValueDto valueDto);
This gives an error on the actual dtosToTarget method:
Error:(17, 19) java: Can't map property "java.lang.String value" to "mapping.Value value". Consider to declare/implement a mapping method: "mapping.Value map(java.lang.String value)".
The only solution I could think of - is defining custom java expressions to call necessary methods, like
#Mapping(target = "key", expression = "java(keyDtoAndValueDtoToKey(keyDto, valueDto))")
#Mapping(target = "value", expression = "java(valueDtoToValue(valueDto))")
Is there a cleaner approach?
The error you are seeing is because by default MapStruct will try to map valueDto.value into Target.value which is String to Value.
However, you can configure this like this:
#Mapper
public MyMapper {
#Mapping( target = "key.id", source = "keyDto.id")
#Mapping( target = "key.name", source = "valueDto.name")
#Mapping( target = "value", source = "valueDto")
Target dtosToTarget(KeyDto keyDto, ValueDto valueDto);
Value valueDtoToValue(ValueDto valueDto);
}
Try :
#Mapping(source = "valueDto.name", target = "name")
void keyDtoAndValueDtoToKey(#MappingTarget KeyDto keyDto, ValueDto valueDto);
This will keep all fields from Key Dto as it is and mapping required fields from valueDto as configured.
I have a graph that includes:
(User)-[:MEMBER_OF]->(Group)
(User)-[:MEMBER_OF]->(Managers)-[:MANAGES]->(Group)
In the application I have the class:
#NodeEntity
public class UserEntity {
#Property
private Long id;
#Relationship(type = "MEMBER_OF", direction = Relationship.OUTGOING)
private List<GroupEntity> memberInGroups;
private List<GroupEntity> managesGroups;
}
In the repository I have a method to get a User like so:
#Query("match (u:User{id:{id}) " +
"optional match (u)-[:MEMBER_OF]->(g:Group) " +
"optional match (u)-[:MEMBER_OF]->(s:Managers)-[:MANAGES]->(m:Group) " +
"return u,collect(g) as memberInGroups,collect(m) as managesGroups")
Optional<UserEntity> findById(Long id);
The problem is that this query returns the result correctly in the browser, meaning I have some values (not null) for memberInGroups and for managesGroups.
However, when I make the call via the application I get the memberInGroups filled in as expected but managesGroups is null.
I assume this is a mapping issue, since managesGroups doesn't have a Relationship annotation nor a Property annotation.
Is there a way to get the managesGroups value to show up correctly inside the UserEntity object?
You can only map the same relationship type within one class to different target types/classes (docs). Of course you would still need the annotation on the other relationship definition.
In your concrete case there is another problem: You cannot "skip" relationships/nodes in the graph and just let it get mapped to the entity.
A solution to achieve this would be to also model the Managers and within this class the Group relationship. Then it would be mandatory to also name and return the relationships in your custom query.
Json to GraphQLArgumetn object conversion failing in graphql-spqr.
tried adding GraphQLInterface(with autodiscovery true and scanpackage) to above abstract classes
and GraphQLtype type all concrete classes.
My graph query:
query contactsQuery($searchQuery : QueryInput) { contacts(searchQuery:$searchQuery){id}}
variables:{"searchQuery":{"bool":{"conditions":[{"must":{"matches":[{"singleFieldMatch":{"boost":null,"field":"firstname","value":"siddiq"}}],"bool":null}}]}})
Java code:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#type(value = Must.class, name="must"),#type(value = MustNot.class, name="mustNot")})
public abstract class Condition
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#type(value = SingleFieldMatch.class, name="singleFieldMatch"),#type(value = MultiFieldMatch.class, name="multiFieldMatch")})
public abstract class Match
#GraphQLQuery(name = "contacts")
public List getContacts(#GraphQLArgument(name ="searchQuery") Query query)
Still it's throwing error unknown field error etc. Not sure which configuration is missing.
Building GraphQLSchema with AnnotatedResolvedBuilder, base package configured JacksonValueMappperFactory and singleton services.
Hi this may be a similar issue to what I ended up having.
Initially I had the following
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#GraphQLInterface(name = "AbstractClass", implementationAutoDiscovery = true)
public abstract class AbstractClass{
with the following query called
addNewObject(object: {name: "soft2", id: "asdas"})
To get conversion functioning what I needed to do was the following change
#JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type")
#GraphQLInterface(name = "AbstractClass", implementationAutoDiscovery = true)
public abstract class AbstractClass{
private String type = this.getClass().getSimpleName();
/**
* #return the type
*/
#GraphQLQuery(name = "type", description = "The concrete type of the node. This should match the initialised class. E.g. \"Concrete\", \"DecafCoffee\"")
public String getType() {
return type;
}
with the query now being
addNewConcreteObject(concrete: {name: "soft2", id: "asdas", type: "Concrete"})
Why this worked (I think):
When converting from JSON to objects in my code using the Jackson converter (ObjectMapper). I had previously noticed that the JSON required knowledge of what class to convert to. Thus the initial use of #JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type") put a type property in the JSON when it was written to string.
The inclusion of the #JSON tag may be picked up by SPQR and it then seems to use a Jackson converter to try to convert your query to the required object.
If I am right, here is the issue.
As the query doesn't contain type the query can not be correctly converted. Moreover as the type property was not a member variable of the object but was instead only added by the ObjectMapper, SPQR didn't pick it up and so it wasn't part of the schema for the object. Thus to get around it, I added type as a member variable which is always equal to the actual class, then changed my JsonTypeInfo to look for an existing property.
I appreciate this isn't a direct answer to your question (and definitely isn't a pretty answer), but hopefully it will help you find your solution.
I have a Couchbase-Document "Group" with a list of group-members-names. I want to query for all groups of one person. I managed to do it with N1QL ARRAY_CONTAINS - see in code example - but i hoped that i could generate the query from the method name as it is usual in Spring Data.
Any help is appreciated :)
I tried
public List<MyGroup> findAllByMembers(String member); and public List<MyGroup> findByMembers(String member); but they just return an empty list - i guess they try to match the whole "members" value and don't recognize it as a list -, no errors.
Code
My Document with a List field
#Data
#Document
public class MyGroup {
private String name;
private List<String> members = new ArrayList<>();
}
My Repository
#RepositoryDefinition(domainClass = MyGroup.class, idClass = String.class)
public interface MyGroupRepository extends CouchbaseRepository<MyGroup, String> {
//#Query("#{#n1ql.selectEntity} WHERE ARRAY_CONTAINS(members,$1) AND #{#n1ql.filter}")
public List<MyGroup> findAllByMembers(String member);
}
Expected
Given a "group1" with "member1" in members.
repository.findAllByMembers("member1"); should return ["group1"].
Couchbase is limited by the Spring Data specification. Unfortunately, we can't simply add new behaviors to it (if you switch to a relational database, it has to work with no breaking points). So, whenever you need to query something that has a N1QL specific function/keyword, you have to write a query via #Query