Is there a way to show constraint message - spring-restdocs

I have class CategoryDTO and i want to show the message "Description can't be null" in rest docs instead of "Must not be null".Though i know i can change this message my creating a constraint properties file and adding below line to it
javax.validation.constraints.NotNull.description=Must not be blank or null
But i want to show the message in NotNull annotation
public class CategoryDTO
{
private String id;
#NotNull(message = "Description can't be null")
#Size(min = 2 , max=30 , message = "Size must be greater than 2 and less than 30")
private String description;
}
Edit:
#Test
void testFindAll()
{
CategoryDTO fruits = new CategoryDTO();
fruits.setDescription("Fruits");
fruits.setId(UUID.randomUUID().toString());
CategoryDTO Nuts = new CategoryDTO();
Nuts.setDescription("Nuts");
Nuts.setId(UUID.randomUUID().toString());
ConstrainedFields fields = new ConstrainedFields(CategoryDTO.class);
BDDMockito.when(categoryService.findAll()).thenReturn(Flux.just(fruits,Nuts));
webTestClient.get().uri(CategoryController.rootURL + "/categories")
.exchange().expectBodyList(CategoryDTO.class).
hasSize(2).consumeWith(WebTestClientRestDocumentationWrapper.document("v1/get-all-categories",
responseFields(
fields.withPath("[]").description("An array of categories"),
fields.withPath("[].id").description("Id of category"),
fields.withPath("[].description").description("Description of category")
)
));
}

By default, REST Docs' ConstraintDescriptions uses a ResourceBundleConstraintDescriptionResolver to obtain a description for each constraint. As its name suggests, it uses a ResourceBundle to provide the descriptions. You can provide your own implementation of ConstraintDescriptionResolver to use a different mechanism. In your case, you want to use the message from the constraint annotation as shown in the following example:
ConstraintDescriptions descriptions = new ConstraintDescriptions(CategoryDTO.class, (constraint) -> {
return (String) constraint.getConfiguration().get("message");
});
List<String> descriptionProperty = descriptions.descriptionsForProperty("description");
System.out.println(descriptionProperty);
When executed, the above will output the following:
[Description can't be null, Size must be greater than 2 and less than 30]
If you don't always configure the message attribute, you may want to fall back to the resource bundle resolver, as shown in the following example:
ResourceBundleConstraintDescriptionResolver fallback = new ResourceBundleConstraintDescriptionResolver();
ConstraintDescriptions descriptions = new ConstraintDescriptions(CategoryDTO.class, (constraint) -> {
String message = (String) constraint.getConfiguration().get("message");
if (message != null) {
return message;
}
return fallback.resolveDescription(constraint);
});

With the help of Andy's answer here is the final outcome
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.snippet.Attributes.key;
import java.util.regex.Pattern;
import org.springframework.restdocs.constraints.ConstraintDescriptions;
import org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.util.StringUtils;
public class ConstrainedFields
{
private final ConstraintDescriptions constraintDescriptions;
public ConstrainedFields(Class<?> input) {
ResourceBundleConstraintDescriptionResolver fallback = new ResourceBundleConstraintDescriptionResolver();
this.constraintDescriptions = new ConstraintDescriptions(input, (constraint) -> {
String message = (String) constraint.getConfiguration().get("message");
if (message != null && !Pattern.compile("\\{(.*?)\\}").matcher(message).matches()) {
return message;
}
return fallback.resolveDescription(constraint);
});
}
public FieldDescriptor withPath(String path)
{
return fieldWithPath(path).attributes(key("constraints").value(StringUtils
.collectionToDelimitedString(constraintDescriptions
.descriptionsForProperty(path), ". ")));
}
}

Related

Setting and getting a Global variable in Drools

I have something like this in drl file:
import java.lang.String
global String result;
rule ''Rule 1'' when some condition
then
result = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("Result", result); // I got an "Unexpected global" exception.
System.out.println("result = "+ Result);
Also, I don't know how to access this global variable from my MyService.java class.
I was trying to set a global variable from the drl file not my java class like Service class.
All I had to do was the following and it worked successfully
import java.lang.String
global String result;
rule ''Rule 1''
when
some condition
then
String grade = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("result", grade);
end
Also, the global variable name should match what I pass on the setGlobal("result",...).
And then get the global variable using the session I have in the Service class. like:
session.getGlobal("result");
Your rule should not be touching the 'kcontext'. What in the world are you trying to do? result = "PASS" is sufficient for setting the value of the global.
global String result
rule "Rule 1"
when
// some condition
then
result = "PASS";
end
Of course it's not going to work like you want it to because you need to change the value of the existing object; you can't overwrite it like that. Some options might be a "ResultsHolder" sort of class with a boolean variable you can set; or maybe even an AtomicBoolean that you can call set on.
To fire rules with a global, you need to add the global objects to the KieBase before invoking your rules:
var value = ...; // some OBJECT which you are going to pass in as a global
KieSession session = ruleBase.newStatefulSession();
session.insert(...); // insert data
session.setGlobal( "myGlobalFoo", value ); // sets the global; note the name must match the rule file!
session.fireAllRules();
After the rules are fired, you'll have your reference to value that you can use. This is also why you can't pass strings as globals and expect them to capture changes -- Java is pass-by-value, not pass-by-reference.
Here's an example for passing results out of the rules. This toy app will check the student's score on a test and then decide if they passed or failed.
Classes:
class Student {
private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
class Exam {
private String name;
private Double score;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public Double getScore() { return this.score; }
public void setScore(String score) { this.score = score; }
}
class ExamResults {
private List<String> results = new ArrayList<>();
public void logResults( String name, Double score, boolean passed ) {
this.results.add(name + " scored " + score + "%, which is a " + (passed ? "passing": "failing") + " grade.");
}
public List<String> getResults() { return this.results; }
}
Rule:
global ExamResults results;
rule "Evaluate exam"
when
Student( $name: name )
Exam ( $score: score, name == $name )
then
boolean passed = $score > 60.0;
results.logResults( $name, $score, passed );
end
Invocation:
List<Student> students = ...;
List<Exam> exams = ... ;
ExamResults results = new ExamResults();
KieSession session = ruleBase.newStatefulSession();
students.forEach( student -> session.insert(students) );
exams.forEach( exam -> session.insert(exam) );
session.setGlobal( "results", results);
session.fireAllRules();
// Print the results:
results.getResults().forEach(System.out::println);
If all you're trying to do is to get some data out of your rules (eg whether certain conditions match), I've written up answers about how to do that previously here and here. If you just want to know what rules triggered, you should write a listener which logs rule hits ("afterMatchFired").

Writable Classes in mapreduce

How can i use the values from hashset (the docid and offset) to the reduce writable so as to connect map writable with reduce writable?
The mapper (LineIndexMapper) works fine but in the reducer (LineIndexReducer) i get the error that it can't get string as argument when i type this:
context.write(key, new IndexRecordWritable("some string");
although i have the public String toString() in the ReduceWritable too.
I believe the hashset in reducer's writable (IndexRecordWritable.java) maybe isn't taking the values correctly?
I have the below code.
IndexMapRecordWritable.java
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
public class IndexMapRecordWritable implements Writable {
private LongWritable offset;
private Text docid;
public LongWritable getOffsetWritable() {
return offset;
}
public Text getDocidWritable() {
return docid;
}
public long getOffset() {
return offset.get();
}
public String getDocid() {
return docid.toString();
}
public IndexMapRecordWritable() {
this.offset = new LongWritable();
this.docid = new Text();
}
public IndexMapRecordWritable(long offset, String docid) {
this.offset = new LongWritable(offset);
this.docid = new Text(docid);
}
public IndexMapRecordWritable(IndexMapRecordWritable indexMapRecordWritable) {
this.offset = indexMapRecordWritable.getOffsetWritable();
this.docid = indexMapRecordWritable.getDocidWritable();
}
#Override
public String toString() {
StringBuilder output = new StringBuilder()
output.append(docid);
output.append(offset);
return output.toString();
}
#Override
public void write(DataOutput out) throws IOException {
}
#Override
public void readFields(DataInput in) throws IOException {
}
}
IndexRecordWritable.java
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
import org.apache.hadoop.io.Writable;
public class IndexRecordWritable implements Writable {
// Save each index record from maps
private HashSet<IndexMapRecordWritable> tokens = new HashSet<IndexMapRecordWritable>();
public IndexRecordWritable() {
}
public IndexRecordWritable(
Iterable<IndexMapRecordWritable> indexMapRecordWritables) {
}
#Override
public String toString() {
StringBuilder output = new StringBuilder();
return output.toString();
}
#Override
public void write(DataOutput out) throws IOException {
}
#Override
public void readFields(DataInput in) throws IOException {
}
}
Alright, here is my answer based on a few assumptions. The final output is a text file containing the key and the file names separated by a comma based on the information in the reducer class's comments on the pre-condition and post-condition.
In this case, you really don't need IndexRecordWritable class. You can simply write to your context using
context.write(key, new Text(valueBuilder.substring(0, valueBuilder.length() - 1)));
with the class declaration line as
public class LineIndexReducer extends Reducer<Text, IndexMapRecordWritable, Text, Text>
Don't forget to set the correct output class in the driver.
That must serve the purpose according to the post-condition in your reducer class. But, if you really want to write a Text-IndexRecordWritable pair to your context, there are two ways approach it -
with string as an argument (based on your attempt passing a string when you IndexRecordWritable class constructor is not designed to accept strings) and
with HashSet as an argument (based on the HashSet initialised in IndexRecordWritable class).
Since your constructor of IndexRecordWritable class is not designed to accept String as an input, you cannot pass a string. Hence the error you are getting that you can't use string as an argument. Ps: if you want your constructor to accept Strings, you must have another constructor in your IndexRecordWritable class as below:
// Save each index record from maps
private HashSet<IndexMapRecordWritable> tokens = new HashSet<IndexMapRecordWritable>();
// to save the string
private String value;
public IndexRecordWritable() {
}
public IndexRecordWritable(
HashSet<IndexMapRecordWritable> indexMapRecordWritables) {
/***/
}
// to accpet string
public IndexRecordWritable (String value) {
this.value = value;
}
but that won't be valid if you want to use the HashSet. So, approach #1 can't be used. You can't pass a string.
That leaves us with approach #2. Passing a HashSet as an argument since you want to make use of the HashSet. In this case, you must create a HashSet in your reducer before passing it as an argument to IndexRecordWritable in context.write.
To do this, your reducer must look like this.
#Override
protected void reduce(Text key, Iterable<IndexMapRecordWritable> values, Context context) throws IOException, InterruptedException {
//StringBuilder valueBuilder = new StringBuilder();
HashSet<IndexMapRecordWritable> set = new HashSet<>();
for (IndexMapRecordWritable val : values) {
set.add(val);
//valueBuilder.append(val);
//valueBuilder.append(",");
}
//write the key and the adjusted value (removing the last comma)
//context.write(key, new IndexRecordWritable(valueBuilder.substring(0, valueBuilder.length() - 1)));
context.write(key, new IndexRecordWritable(set));
//valueBuilder.setLength(0);
}
and your IndexRecordWritable.java must have this.
// Save each index record from maps
private HashSet<IndexMapRecordWritable> tokens = new HashSet<IndexMapRecordWritable>();
// to save the string
//private String value;
public IndexRecordWritable() {
}
public IndexRecordWritable(
HashSet<IndexMapRecordWritable> indexMapRecordWritables) {
/***/
tokens.addAll(indexMapRecordWritables);
}
Remember, this is not the requirement according to the description of your reducer where it says.
POST-CONDITION: emit the output a single key-value where all the file names are separated by a comma ",". <"marcello", "a.txt#3345,b.txt#344,c.txt#785">
If you still choose to emit (Text, IndexRecordWritable), remember to process the HashSet in IndexRecordWritable to get it in the desired format.

How can I return only several Json fields of an inner object with Jackson

I'm using Jackson + spring RestControlles
Lets say I have 3 classes
class Card {
String id;
String pan;
String holder;
String expiry;
}
class CardProfile {
String id;
Card card;
...
}
class Recipient {
String name;
Card card;
...
}
So when I serialize a CardProfile object I want all fields of object card to be percent in result json.
But in case Recipient object , Json has to have only part of object card(For example : pan, holder) .
Is there a way to present Card object in json with different set of fields?
Use of annotations like #JsonIgnore will remove fields from both cases.
Thanks.
Thinking again - is this approach scalable? What if you need something similar again?
Why not rely on inheritance and create a base class and multiple subclasses with attributes you are interested in?
class Card{
// bare min attribute
.....
}
class SubCard1 extends Card{
//add more attribute
.....
}
class SubCard2 extends Card{
//add more attribute
.....
}
Use SubCard1 or SubCard2 based on what you need? Same can be used for other classes. If this is more dynamic - look at design patterns.
when you serialize the object you can keep only the parameter that are interest for you.
I post a simple java code to resolve this stuff you can convert the code in the lenguage that you need.
the following code is prt of the Recipient class which contains two field: String name and Card card
To write the json object:
#Override
public final void toJSON(final OutputStream out) throws IOException {
final JsonGenerator jg = JSON_FACTORY.createGenerator(out);
jg.writeStartObject();
jg.writeFieldName("Recipient");
jg.writeStartObject();
jg.writeNumberField("id",card.getID());
jg.writeStringField("name", name);
jg.writeStringField("pan", card.getPan());
jg.writeStringField("holder", card.getHolder());
jg.writeEndObject();
jg.writeEndObject();
jg.flush();
}
to get the the object from the json:
public static Recipient fromJSON(final InputStream in) throws IOException {
int jId = -1;
String jName = null;
String jHolder = null;
String jPan = null;
//Obtain a new JsonParser to parse Recipient from JSON
final JsonParser jp = JSON_FACTORY.createParser(in);
while (jp.getCurrentToken() != JsonToken.FIELD_NAME || "Recipient".equals(jp.getCurrentName()) == false) {
// there are no more events
if (jp.nextToken() == null) {
throw new IOException("Unable to parse JSON: no employee object found.");
}
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
if (jp.getCurrentToken() == JsonToken.FIELD_NAME) {
switch (jp.getCurrentName()) {
case "id":
jp.nextToken();
jId = jp.getIntValue();
break;
case "name":
jp.nextToken();
jName = jp.getText();
break;
case "pan":
jp.nextToken();
jPan = jp.getText();
break;
case "holder":
jp.nextToken();
jHolder = jp.getText();
break; }
} }
//create new card object, the values not present in the json will set to null
Card card = new Card(jId,null,jPan,jHolder,null);
return new Recipient(jName, card);
}
}
I tried to adapt the code for you but it is just to give you an idea,I hope this will be usefull for you.

Get and Set attribute values of a class using aspectJ

I am using aspectj to add some field to a existing class and annotate it also.
I am using load time weaving .
Example :- I have a Class customer in which i am adding 3 string attributes. But my issues is that I have to set some values and get it also before my business call.
I am trying the below approach.
In my aj file i have added the below, my problem is in the Around pointcut , how do i get the attribute and set the attribute.
public String net.customers.PersonCustomer.getOfflineRiskCategory() {
return OfflineRiskCategory;
}
public void net.customers.PersonCustomer.setOfflineRiskCategory(String offlineRiskCategory) {
OfflineRiskCategory = offlineRiskCategory;
}
public String net.customers.PersonCustomer.getOnlineRiskCategory() {
return OnlineRiskCategory;
}
public void net.customers.PersonCustomer.setOnlineRiskCategory(String onlineRiskCategory) {
OnlineRiskCategory = onlineRiskCategory;
}
public String net.customers.PersonCustomer.getPersonCommercialStatus() {
return PersonCommercialStatus;
}
public void net.customers.PersonCustomer.setPersonCommercialStatus(String personCommercialStatus) {
PersonCommercialStatus = personCommercialStatus;
}
#Around("execution(* net.xxx.xxx.xxx.DataMigration.populateMap(..))")
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
Object arguments[] = joinPoint.getArgs();
if (arguments != null) {
HashMap<String, String> hMap = (HashMap) arguments[0];
PersonCustomer cus = (PersonCustomer) arguments[1];
return joinPoint.proceed();
}
If anyone has ideas please let me know.
regards,
FT
First suggestion, I would avoid mixing code-style aspectj with annotation-style. Ie- instead of #Around, use around.
Second, instead of getting the arguments from the joinPoint, you should bind them in the pointcut:
Object around(Map map, PersonCustomer cust) :
execution(* net.xxx.xxx.xxx.DataMigration.populateMap(Map, PersonCustomer) && args(map, cust) {
...
return proceed(map, cust);
}
Now, to answer your question: you also need to use intertype declarations to add new fields to your class, so do something like this:
private String net.customers.PersonCustomer.OfflineRiskCategory;
private String net.customers.PersonCustomer.OnlineRiskCategory;
private String net.customers.PersonCustomer.PersonCommercialStatus;
Note that the private keyword here means private to the aspect, not to the class that you declare it on.

EXT-GWT (GXT) Display Icon and Text for displayfield in Combobox

Does anyone know how to display an Icon and a Text for the displaying field in ext-gwts combobo? I tried everything.
In the third ComboBox of this example (klick me) there is an icon and the text for the selectable values. This was no problem with the example template. But i want to show the icon and the text for the selected value too. How can i manage this?
I have a Model class for the icon and the text.
public class Language extends DbBaseObjectModel {
private static final long serialVersionUID = 8477520184310335811L;
public Language(String langIcon, String langName) {
setLangIcon(langIcon);
setLangName(langName);
}
public String getLangIcon() {
return get("langIcon");
}
public String getLangName() {
return get("langName");
}
public void setLangIcon(String langIcon) {
set("langIcon", langIcon);
}
public void setLangName(String langName) {
set("langName", langName);
}
}
This is how i initalize the ComboBox. I want to change the displayField "langName".
final ListStore<Language> countries = new ListStore<Language>();
final Language german = new Language("de_DE", "Deutsch");
final Language english = new Language("en_GB", "Englisch");
countries.add(german);
countries.add(english);
final ComboBox<Language> combo = new ComboBox<Language>();
combo.setWidth(100);
combo.setStore(countries);
combo.setDisplayField("langName");
combo.setTemplate(getFlagTemplate());
combo.setTypeAhead(true);
combo.setTriggerAction(TriggerAction.ALL);
combo.setValue(german);
This is the template for the ComboBox two show the selectable values.
private native String getFlagTemplate() /*-{
return [ '<tpl for=".">', '<div class="x-combo-list-item">',
'<img src="resources/images/lang/{langIcon}.png">',
' {langName}</div>', '</tpl>' ].join("");
}-*/;
How can i use an template for the displayField or is there an other possibility?
Thanks!
You need to implement a com.extjs.gxt.ui.client.widget.form.ListModelPropertyEditor.
The com.extjs.gxt.ui.client.widget.form.PropertyEditor#getStringValue returns the string that should be displayed and the com.extjs.gxt.ui.client.widget.form.PropertyEditor#convertStringValue converts the displayed string back into the model.
This isn't a very performant implementation but it works:
public class TemplateModelPropertyEditor<D extends ModelData> extends
ListModelPropertyEditor<D> {
/** Template to render the model. */
private XTemplate template;
#Override
public D convertStringValue(final String value) {
for (final D d : models) {
final String val = getStringValue(d);
if (value.equals(val)) {
return d;
}
}
return null;
}
#Override
public String getStringValue(final D value) {
if (template != null) {
final Element div = DOM.createDiv();
template.overwrite(div, Util.getJsObject(value));
return div.getInnerText();
}
final Object obj = value.get(displayProperty);
if (obj != null) {
return obj.toString();
}
return null;
}
public void setSimpleTemplate(final String html) {
template = XTemplate.create(html);
}
}
Usage:
TemplateModelPropertyEditor<Language> propertyEditor = new TemplateModelPropertyEditor<Language>();
propertyEditor.setSimpleTemplate(getFlagTemplate());
combo.setPropertyEditor(propertyEditor);
which imports?
I added these ones:
import com.extjs.gxt.ui.client.core.XTemplate;
import com.extjs.gxt.ui.client.util.Util;
import com.extjs.gxt.ui.client.widget.form.ListModelPropertyEditor;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
Everthing works fine, but it don't display an icon. When i debug the return div.getInnerText() method throws an error called: Method "getInnerText" with signature "()Ljava/lang/String;" is not applicable on this object.
The created div element looks okay
<DIV><DIV class=x-combo-list-item><IMG src="http://127.0.0.1:8888/resources/images/lang/de_DE.png"> Deutsch</DIV></DIV>