Thymeleaf form bind object - java

I am trying to bind attributes that I get from calling controller in thymeleaf template to form and pass it as param in save method. here is my code:
#GetMapping("/alert/{id}")
public String getForm(Model model, #PathVariable(required = false, name = "id") String id) throws IOException {
final Datum datum = alertService.findById(id);
final List<ElasticAlert> elasticAlerts = ApplicationUtil.datumToElasticAlertModel(Collections.singletonList(datum), objectMapper);
if (elasticAlerts.size() != 1)
throw new IllegalArgumentException("Wrong id for editing data");
final ElasticAlert elasticAlert = elasticAlerts.stream().findFirst().orElseThrow(IllegalArgumentException::new);
elasticAlert.setParams(datum.getParams());
model.addAttribute("elasticAlert", elasticAlert);
return "update";
}
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Params {
private String aggType;
private String esQuery;
private Integer termSize;
private String thresholdComparator;
private Integer timeWindowSize;
private String timeWindowUnit;
private String groupBy;
private List<Integer> threshold;
private List<String> index;
private String timeField;
private String aggField;
private String termField;
private String level;
private String message;
private List<String> to;
private String subject;
private int size;
}
#Data
public class ElasticAlert {
private String id;
#NotBlank (message = "Event Name can not be blank")
#Size(max = 40, min = 5, message = "event name should be between 5-20 characters")
private String eventName;
#NotBlank (message = "Application Name can not be blank")
private String applicationName;
#NotBlank (message = "Email Subject can not be blank")
private String emailSubject;
#NotBlank (message = "Email to can not be blank")
#Pattern(regexp = "[a-zA-Z0-9]+#[abc|xyz]+goninv\\.com$", message = "incorrect email format")
private String emailTo;
#Valid
private List<ElasticException> elasticExceptionList = new ArrayList<>();
private String schedule;
private String notifyWhen;
private Params params;
}
I want to pass params object as it is to my form controller
<form method="post" th:action="#{/alert}" th:object="${elasticAlert}"
name="createAlertForm" id="createAlertForm" class="mb-3">
<input type="hidden" th:field="${elasticAlert.id}"/>
<input type="hidden" th:field="${elasticAlert.applicationName}"/>
<input type="hidden" th:valuetype="test.pojo.Params" th:field="${elasticAlert.params}" />
...
#PostMapping("/alert")
public String edit(#Valid ElasticAlert alert, BindingResult bindingResult, RedirectAttributes redirAttrs, Model model) throws IOException {
int i = 1;
final Params params = alert.getParams();
...
}
my params object is always null, it's not binding object to the input, is there any way to make it bind or an alternate way to approach this.
I am new to thymeleaf.

I guess the problem is in the first method,your request is #GetMapping("/alert/{id}"),but your thymeleaf is /alert,but the /alert request don't send messages.

Related

Spring boot JPA with 1:M primary key null

I have 2 entitied (Post and FileUploads), the Post entity is responsible for a user to create an advert/post and the FileUploads entity is responsible for handling image uploads together with a post. (A post can have multiple FileUploads/Images associated with it).
The issue is that I get an error:
Column 'post_id' cannot be null
I'm unsure as to what's causing it, The PK in the FileUpload entity is a String and the Post entity PK is a Long, I don't know if that could be the cause?
I am auto-generating my Post PK as well.
FileUpload.java (Entity)
#Entity
#Table(name="file_upload")
public class FileUpload {
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
#GeneratedValue(generator = "uuid")
#Column(name="id")
private String fileId;
private int imageCount;
private String name;
private String type;
private String fileUploader;
#Lob
private byte[] data;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="post_id", nullable = false)
private Post post;
public FileUpload() {
}
public FileUpload(int imageCount, String name, String type, String fileUploader, byte[] data, Post post) {
this.imageCount = imageCount;
this.name = name;
this.type = type;
this.fileUploader = fileUploader;
this.data = data;
this.post = post;
}
Post.java (Entity)
Left out the other fields since i have a quite a bit
#Entity
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank(message = "Required")
#Size(max=45, message = "Maximum of 45 letters")
#Column(unique = true)
private String title;
private String postCreatorEmail;
private String postCreator;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "post")
private List<FileUpload> fileUploads = new ArrayList<>();
public Post() {
}
FileServiceImpl.java
Method to store images
#Service
public class FileUploadServiceImpl implements FileUploadService{
#Autowired
private FileUploadRepository fileUploadRepository;
#Autowired
private PostRepository postRepository;
private int imageCount;
#Override
public FileUpload uploadPostImage(MultipartFile file, String emailAddress) throws IOException {
Post thePost = postRepository.findPostByPostCreatorEmail(emailAddress);
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
FileUpload fileDB = new FileUpload(++imageCount, fileName, file.getContentType(), emailAddress, file.getBytes(), thePost);
return fileUploadRepository.save(fileDB);
}
}
PostServiceImpl.java
#Service
public class PostServiceImpl implements PostService{
#Autowired
private PostRepository postRepository;
#Autowired
private UserRepository userRepository;
#Autowired
private FileUploadServiceImpl fileUploadService;
private int count;
#Override
public Post createOrUpdatePost(String post, String emailAddress){
// Removed update code
Post thePost = new Post();
try {
User user = userRepository.findUserByEmailAddress(emailAddress);
ObjectMapper objectMapper = new ObjectMapper();
thePost = objectMapper.readValue(post, Post.class);
user.setTotalAds(++count);
thePost.setPostCreator(user.getFullName());
thePost.setPostCreatorEmail(emailAddress);
thePost.setFileUploads(thePost.getFileUploads());
thePost.setUser(user);
userRepository.save(user);
return postRepository.save(thePost);
// The ad with the same title already exists - go to catch block
} catch (Exception e) {
throw new PostAlreadyExistsException("Post with title " + thePost.getTitle() + " already exists");
}
}
}
PostController.java (Only adding the handler method)
#PostMapping("/create")
public ResponseEntity<?> createPost(#RequestPart("file") MultipartFile file, #Valid #RequestPart String post, BindingResult result, Principal principal) {
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
String message = "";
try {
fileUploadService.uploadPostImage(file, principal.getName());
postService.createOrUpdatePost(post, principal.getName());
message = "Uploaded the file successfully: " + file.getOriginalFilename();
return ResponseEntity.status(HttpStatus.OK).body(new ApiResponse(message, true));
} catch (Exception e) {
message = "Could not upload the file: " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ApiResponse(message, false));
}
}
Postman results
Console output
Post DDL MySQL
FileUpload DDL MySQL
Try to add modify your uploadPostImage to this code:
#Override
public FileUpload uploadPostImage(MultipartFile file, String emailAddress) throws IOException {
Post thePost = postRepository.findPostByPostCreatorEmail(emailAddress);
// new part:
if (thePost == null) {
throw new RuntimeException("No post found yet for eMail-Address '" + emailAddress + "' to store images for!");
}
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
FileUpload fileDB = new FileUpload(++imageCount, fileName, file.getContentType(), emailAddress, file.getBytes(), thePost);
return fileUploadRepository.save(fileDB);
}

Spring RestTemplate postForEntity returns Null Objects

I'm new to Spring and I'm having trouble consuming an API and serialising the Response to Java POJOs using Jackson. This the API endpoint I'm trying to consume.
This what my request looks like:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("Authorization", "Bearer " + applicationProperties.getApiKey());
ArrayList<String> externalIds = new ArrayList<>();
externalIds.add(userId);
Map<String, Object> parameters = new HashMap<>();
parameters.put("external_ids", externalIds);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(parameters, headers);
ResponseEntity<ProfileExportDTO> response = this.restTemplate.postForEntity(url, request , ProfileExportDTO.class);
This is my POJO class (setters and getters removed for simplicity):
#JsonIgnoreProperties(ignoreUnknown = true)
public class ProfileExportDTO implements Serializable {
#JsonProperty("first_name")
private String firstname;
#JsonProperty("last_name")
private String lastname;
private String language;
private String email;
private String dob;
#JsonProperty("home_city")
private String city;
private String country;
private String phone;
#JsonProperty("time_zone")
private String timezone;
#JsonProperty("last_coordinates")
private float[] lastCoordinates;
private String gender;
#JsonProperty("total_revenue")
private float revenue;
private String attributed_campaign;
private String attributed_source;
private String attributed_adgroup;
private String push_subscribe;
private String email_subscribe;
My problem is that when this runs the produced object is null. Does anyone know why?

Error reading entity from input stream Dropwizard example

I'm attempting to use this dropwizard example and build off of it. I tried to add a column userName to the people table in Person.java like below
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "fullName", nullable = false)
private String fullName;
#Column(name = "jobTitle", nullable = false)
private String jobTitle;
#Column(name = "userName", nullable = false)
private String userName;
public Person() {
}
public Person(String fullName, String jobTitle, String userName) {
this.fullName = fullName;
this.jobTitle = jobTitle;
this.userName = userName;
}
I added the appropriate getters and setters, and equals method.
However I'm getting an error reading entity from input stream in this block.
#Test
public void testPostPerson() throws Exception {
final Person person = new Person("Dr. IntegrationTest", "Chief Wizard", "Dr. Wizard");
final Person newPerson = RULE.client().target("http://localhost:" + RULE.getLocalPort() + "/people")
.request()
.post(Entity.entity(person, MediaType.APPLICATION_JSON_TYPE))
--> .readEntity(Person.class);
assertThat(newPerson.getId()).isNotNull();
assertThat(newPerson.getFullName()).isEqualTo(person.getFullName());
assertThat(newPerson.getJobTitle()).isEqualTo(person.getJobTitle());
assertThat(newPerson.getUserName()).isEqualTo(person.getUserName());
}
the input stream error is caused by the following
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "code" (class com.example.helloworld.core.Person), not marked as ignorable (4 known properties: "fullName", "id", "userName", "jobTitle"])
will #JsonIgnoreProperties annotation at the class level solve this problem? Is this safe practice?
EDIT: PersonResource.java
#Path("/people/{personId}")
#Produces(MediaType.APPLICATION_JSON)
public class PersonResource {
private final PersonDAO peopleDAO;
public PersonResource(PersonDAO peopleDAO) {
this.peopleDAO = peopleDAO;
}
#GET
#UnitOfWork
public Person getPerson(#PathParam("personId") LongParam personId) {
return findSafely(personId.get());
}
#GET
#Path("/view_freemarker")
#UnitOfWork
#Produces(MediaType.TEXT_HTML)
public PersonView getPersonViewFreemarker(#PathParam("personId") LongParam personId) {
return new PersonView(PersonView.Template.FREEMARKER, findSafely(personId.get()));
}
#GET
#Path("/view_mustache")
#UnitOfWork
#Produces(MediaType.TEXT_HTML)
public PersonView getPersonViewMustache(#PathParam("personId") LongParam personId) {
return new PersonView(PersonView.Template.MUSTACHE, findSafely(personId.get()));
}
private Person findSafely(long personId) {
return peopleDAO.findById(personId).orElseThrow(() -> new NotFoundException("No such user."));
}
I think it's because the resource fails and throws a web application exception and code is actually the http status code.
Try it like this:
Response response = RULE.client().target("http://localhost:" + RULE.getLocalPort() + "/people")
.request()
.post(Entity.entity(person, MediaType.APPLICATION_JSON_TYPE));
assertEquals(200, response.getStatus());
Person newPerson = response.readEntity(Person.class);
....
You may also debug like this:
String responseString = response.readEntity(String.class);
Which will dump you the body of the response.

Jackson XML tag and attribute with the same name

I want to get the following XML:
<User id="two">
<id>one</id>
</User>
And I try to use Jackson XML mapper for this:
#JacksonXmlRootElement
public class User {
private String id;
private String attributeId;
public User(final String id, final String attributeId) {
this.id = id;
this.attributeId = attributeId;
}
#JacksonXmlProperty(localName = "id")
public String getId() {
return id;
}
#JacksonXmlProperty(localName = "id", isAttribute = true)
public String getAttributeId() {
return attributeId;
}
public static void main(String[] args) throws IOException {
final XmlMapper xmlMapper = new XmlMapper();
final File file = new File("user.xml");
final User user = new User("one", "two");
xmlMapper.writeValue(file, user);
}
}
But all I get is this exception
java.lang.IllegalArgumentException: Conflicting getter definitions for property "id": com.sbconverter.parser.slovoed.User#getId(0 params) vs com.sbconverter.parser.slovoed.User#getAttributeId(0 params)
Can I have same name of the attribute and tag, on one object?
This is a known problem, so you'll need to do extra classes for this case.
Adding a space in front of id at localName (localName = " id") can do the trick, but it's more recommended to make a new bean.

Validate Request body in spring using #Valid

I am want to validate a JSON object, for length of an attribute. I am using #Size annotation to specify maximum length as shown below.
#JsonRootName("question")
public class QuestionJson {
#JsonProperty(value = "id", required = false)
private Long id;
#JsonProperty(value = "text", required = true)
private String label;
#JsonProperty(value = "answers", required = true)
private List<AnswerJson> answers;
}
#JsonRootName("answer")
public class AnswerJson {
#JsonProperty(value = "id", required = false)
private Long id;
#JsonProperty(value = "type", required = true)
private String type;
#JsonProperty(value = "label", required = true)
#Size(message = "size should not be long", max = 10)
private String label;
}
My request mapping in controller looks like:
#RequestMapping(value = "/api/answer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public AnswerJson createQuestion(#RequestHeader(HttpHeaders.AUTHORIZATION) final String authorizationHeader, #Valid #RequestBody final QuestionJson questionJson) {
// some code here....
return answer;
}
UPDATE: Validation works on the outer elements eg. text in my case but fails on the nested list.
Every time we use #Valid annotation we also include a BindingResult instance as a method parameter, it contains the #Valid marked parameter errors if any:
public final #ResponseBody String theMethod(
final #Valid ValidableObjectImpl validableObject,
BindingResult result) {
try {
if (result.hasErrors()) {
for (FieldError error : result.getFieldErrors()){
// do something
}
// return error
}
} catch (Exception e) {
// ...
}
}
Found the solution. We need add #Valid annotation to the before the declaration of the nested object. eg
#JsonRootName("question")
public class QuestionJson {
#JsonProperty(value = "id", required = false)
private Long id;
#JsonProperty(value = "text", required = true)
private String label;
#JsonProperty(value = "answers", required = true)
#Valid
private List<AnswerJson> answers;
}

Categories

Resources