mapstruct qualifiedByName without parameters simplify expression - java

I would like to set a constant on the field, but with method call, i do not want to create an expression it looks terrible, i would like to simplify this call
#Mapping(target = "channelNotification", expression= "java(new ChannelNotification[]{ " +
"new ChannelNotification(\"email\", 10)})")
to get something like this:
#Mapping(target = "channel", qualifiedByName = "getChannel")
Notification convert(Email emailEntity);
#Named("getChannel")
default Channel[] getChannel() {//with empty params
return new Channel[]{new Channel("email", 10)};
}
Source entity doesn't have field channelNotification, and i don't need to use it. I just want to set a constant like constant = *, but with method call

This is currently not possible. However, what you can do is to use a combination of constant and qualifiedByName.
e.g.
#Mapping(target = "channel", constant = "email" qualifiedByName = "getChannel")
Notification convert(Email emailEntity);
#Named("getChannel")
default Channel[] getChannel(String channelType) {
Channel channel;
if ("email".equals(channelType)) {
channel = new Channel("email", 10);
} else {
throw new IllegalArgumentException("unknown channel type " + channelType);
}
return new Channel[]{channel};
}
What is not known that much is that constant works in a similar way with qualifiers as other Mapping#source. Since 1.4 custom mappings between String (what is in constant) and the target type will be looked for in order to convert the constant value.

Related

Jooq dsl one to many relations

I'm using Spring Data + Jooq DSL. As result entity I'm using not jooq generated entity, but simple one, without any annotations and for relations One To Many getting result:
[{
"id":2,
"name":"James",
"addresses":[
{
"id":null,
"country":null,
"street":null
}
]
}]
Is any way to return an empty array for addresses?
My code to perform a request:
public Set<User> getUserById(Set<Long> id) {
Set<User> result = new HashSet<>();
ResultQuery users = dsl.select(
field("u.id", Long.class).as("id"),
field("u.name", String.class).as("name"),
field("a.id", Long.class).as("addresses_id"),
field("a.country", String.class).as("addresses_country"),
field("a.street", String.class).as("addresses_street")
).from("schema.user_table u")
.leftJoin("schema.address_table a")
.on("u.id = a.user_id")
.where(field("u.id").in(id));
try(ResultSet rs = users.fetchResultSet()) {
JdbcMapper<User> mapper = JdbcMapperFactory
.newInstance()
.addKeys("id")
.newMapper(User.class);
result = mapper.stream(rs).collect(Collectors.toSet());
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Why not just use SQL/JSON to produce JSON documents directly from within your database?
public String getUserById(Set<Long> id) {
return dsl.select(coalesce(jsonArrayAgg(
jsonObject(
key("id").value(field("u.id", Long.class)),
key("name").value(field("u.name", String.class)),
key("addresses").value(coalesce(
jsonArrayAgg(jsonObject(
key("id").value(field("a.id", Long.class)),
key("country").value(field("a.country", String.class)),
key("street").value(field("a.street", String.class))
)),
jsonArray()
))
),
jsonArray()
)))
.from("schema.user_table u")
.leftJoin("schema.address_table a")
.on("u.id = a.user_id")
.where(field("u.id").in(id))
.fetchSingle().value1().data();
}
If you really need the intermediate User representation, then you can either:
Use Jackson or Gson to map the JSON document to the nested User DTO structure using reflection (works with jOOQ 3.14)
Use jOOQ 3.15's new MULTISET value constructor operator or MULTISET_AGG aggregate function along with ad-hoc converters, see below:
public Set<User> getUserById(Set<Long> id) {
return dsl.select(
field("u.id", Long.class),
field("u.name", String.class),
multisetAgg(
field("a.id", Long.class),
field("a.country", String.class),
field("a.street", String.class)
).convertFrom(r -> r == null
? Collections.<Address>emptyList()
: r.map(Records.mapping(Address::new)))
)
.from("schema.user_table u")
.leftJoin("schema.address_table a")
.on("u.id = a.user_id")
.where(field("u.id").in(id))
.fetchSet(Records.mapping(User::new));
}
Side note on code generation and execution
While not strictly relevant to this question, unless your schema is dynamic (not known at compile time), I really urge you to reconsider using source code generation. If you're not using it, you're missing out on a lot of jOOQ API advantages, just like when you're executing a jOOQ query with something other than jOOQ.
For me worked specify as a key addressId:
.addKeys("id", "addresses_id")

In Swagger,for query-parameters when allowMultiple=true, how to send multiple values in comma seperated format?

I am using swagger-2.6.1, in a spring-mvc application.
For a query parameter, I need to allow multiple values. I have used allowMultiple= true. But this repeats the parameter name in the url for each value.
I need to send the parameter name once, with all the values in a comma-separated format.
How to do this
#RequestMapping(value = "/test", method = RequestMethod.GET)
#ApiImplicitParams(value = {
#ApiImplicitParam(name = "testParam", value = "test parameter", dataType = "string", allowMultiple= true, allowableValues = "value1,value2,value3", paramType = "query"),
})
public String testMethod(){
// code
}
Existing url : http://localhost:8080/testApi/test?testParam=value1&testParam=value2&testParam=value3
Required url : http://localhost:8080/testApi/test?testParam=value1,value2,value3
Try adding the following parameter to the annotation #ApiImplicitParam:
collectionFormat = "Set"
I used #ApiParam annotation and it worked
In your case there is only one param testParam with value value1,value2,value3
What are you trying to do is to say swagger that there are more than 1 param when there is only 1.
If you need multiple params use unique names for them like param1, param2, param3

How to get Java interface #Path value from other class

I have the following kind of interface. Is there any way to get #Path("/bucket-definitions") value "/bucket-definitions" from other class?
#Path("/bucket-definitions")
#Api(
value = "Bucket definition",
authorizations = {#Authorization("token")}
)
public interface BucketDefinitionResource {
#GET
#Path("/{operator-id}")
#Produces({"application/json"})
#ApiOperation(
value = "Get all bucket definitions.",
notes = "Returns all bucket definitions.",
response = BucketDefinitionList.class
)
BucketDefinitionList get(#ApiParam(value = "Bucket definitions of the operator to be fetched.",required = true) #PathParam("operator-id") String var1, #ApiParam(value = "Page number",required = false) #DefaultValue("1") #QueryParam("page") Integer var2, #ApiParam("Items per page") #DefaultValue("20") #QueryParam("per_page") Integer var3);
}
I discovered the following solution after trying in several ways. I was only interested to get the value of #Path("/bucket-definitions") that is "bucket-definitions". It is not from any website. So it is completely my way of getting the value of #Path annotation. Other experts can suggest me a better way. Hopefully, this solution will be helpful for others.
Annotation annotation = BucketDefinitionResource.class.getAnnotations()[0];
if (annotation.toString().contains("Path")) {
String SERVICE_NAME = annotation.toString().substring(annotation.toString().indexOf("/"), annotation.toString().indexOf(")"));
}

Pass array as value of annotation param in JavaPoet

Using JavaPoet I'm trying to annotate a class with an annotation which has an array as a parameter value i.e.
#MyCustom(param = { Bar.class, Another.class })
class Foo {
}
I use AnnotationSpec.builder and its addMember() method:
List<TypeMirror> moduleTypes = new ArrayList<>(map.keySet());
AnnotationSpec annotationSpec = AnnotationSpec.builder(MyCustom.class)
.addMember("param", "{ $T[] } ", moduleTypes.toArray() )
.build();
builder.addAnnotation(annotationSpec);
CodeBlock has a joining collector, and you can use that to stream this, doing something like the following (if for instance this was an enum). You can do it for any types, just the map would change.
AnnotationSpec.builder(MyCustom.class)
.addMember(
"param",
"$L",
moduleTypes.stream()
.map(type -> CodeBlock.of("$T.$L", MyCustom.class, type))
.collect(CodeBlock.joining(",", "{", "}")))
.build()
Maybe not an optimal solution but passing an array to an annotation in JavaPoet can be done in the following way:
List<TypeMirror> moduleTypes = new ArrayList<>(map.keySet());
CodeBlock.Builder codeBuilder = CodeBlock.builder();
boolean arrayStart = true;
codeBuilder.add("{ ");
for (TypeMirror modType: moduleTypes)
if (!arrayStart)
codeBuilder.add(" , ");
arrayStart = false;
codeBuilder.add("$T.class", modType);
codeBuilder.add(" }");
AnnotationSpec annotationSpec = AnnotationSpec.builder(MyCustom.class)
.addMember("param", codeBuilder.build() )
.build();
builder.addAnnotation(annotationSpec);

Using #JsonTypeInfo why can't the polymorphic field be omitted?

Here is a polymorphic field:
#JsonProperty("config")
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(name = "keystone", value = KeystoneConfig.class),
#JsonSubTypes.Type(name = "internal", value = InternalConfig.class)
})
#Nullable
abstract Configuration getConfig();
where Configuration is an interface, and type is a String field elsewhere in the structure/class.
This works fine with a response payload (something) like this:
{ "type": "internal",
"config": { "int1": "value1" }
}
but fails with a deserialization parsing error when the config field is omitted, like this:
{ "type": "internal"
}
Normally, when fields are omitted, we get the Java default value (I expected null in this case). I wish to retain the discriminator field type, but allow the config field to be optional.
How can I get that behaviour in this case?
JacksonAnnotationIntrospector, JsonDeserializer and JsonTypeResolver seems do not help for your case.
At the time being, I can only come up a work around by deserializing the json string with 3 step as follows:
ObjectMapper mapper = new ObjectMapper();
// 1. deserializing the json string into JsonNode
JsonNode jsonNode = mapper.readTree(json);
// 2. Add a empty node if "config" property is not found
JsonNode configNode = jsonNode.get("config");
if (jsonNode.isObject() && (configNode == null || configNode.size() == 0)) {
((ObjectNode) jsonNode).put("config", "");
}
// 3. Convert to desired class
mapper.treeToValue(jsonNode, Foo.class);

Categories

Resources