Api endpoint that edits one element in list - java

I am currently building a rest api that lets the user enter a recipe and describe it. I am using spring-boot as backend and angularjs as frontend.
This is springboot recipe file
package com.example.springboot;
import com.example.springboot.Recipe;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Entity // This tells Hibernate to make a table out of this class
public class Recipe {
public Recipe(){
}
public Recipe(Integer id, String name, String description, String type, Integer preptime, Integer cooktime, String content, Integer difficulty){
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.preptime = preptimee;
this.cooktime = cooktime;
this.content = content;
this.difficulty = difficulty;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String description;
private String type;
private Integer preptime;
#Column(columnDefinition = "TEXT")
private String content;
private Integer difficulty;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getDifficulty() {
return difficulty;
}
public void setDifficulty(Integer difficulty) {
this.difficulty = difficulty;
}
public Integer getpreptime {
return preptime;
}
public void setpreptime(Integer preptime) {
this.preptime = preptime;
}
}
I created an Endpoint where the user can edit the whole recipe. The user can edit the name , description, content and so on in the recipes/edit/{id} endpoint.
The Endpoint looks like this.
#PutMapping("/recipes/edit/{id}")
void updateRecipe(#PathVariable int id, #RequestBody Recipe recipe ) {
System.out.println("entering");
Recipe recipe_ = recipeRepository.findById(id).get();
recipe_.setName(recipe.getName());
recipe_.setDescription(recipe.getDescription());
recipe_.setType(recipe.getType());
recipe_.setpreptime(recipe.getpreptime());
recipe_.setContent(recipe.getContent());
System.out.println("entering " + recipe.getTitle());
System.out.println("entering" + recipe.getType());
System.out.println("entering" + recipe.getDescription());
System.out.println("adding");
recipeRepository.save(recipe_);
}
Now I just want to create an Endpoint which only serves the purpose for renaming the name of the recipe. This putmapping should accept a list as its input then only rename the name of the recipe.
#PutMapping("/recipes/rename")
public List<Recipe> {
System.out.println("entering renaming");
// recipe_.setName(recipe.getName()); ?
}
I don't know how I can implement this. This is what I have come up with so far. An endpoint which takes a list as a parameter.
This is the service.ts file that updates the Recipes in the edit function
service.ts:
updateRecipe (id: number, recipe: any): Observable<any > {
const url = `${this.usersUrl}/edit/${id}`;
return this.http.put(url ,recipe);
}
where the updateRecipe gets called:
save(): void {
const id = +this.route.snapshot.paramMap.get('name');
this.recipeService.updateRecipe2(id, this.recipes)
.subscribe(() => this.gotoUserList());
}
This implementation work , I don't know how I can get it work or how I can rewrite the functions so that it can update only the name of the recipe and not the whole file.
Could someone help me?

Your update the name method should look like that:
#PutMapping("...{id}")
public void updateName(#PathVariable Integer id, #RequestParam String name){
Recipe recipe = repository.findById(id).orElseThrow(...);
recipe.setName(name);
}
if you want to rename list of recipes
public void renameRecipes(String oldName, String newName){
repository.findByName(oldName)
.forEach(r -> r.setName(newName));
}
#PutMapping("recipes/rename")
public void updateNames(#PequestParam String oldName, #RequestParam String newName){
renameRecipes(oldName, newName);
}
Try that.

Related

JSON parse error: Cannot deserialize instance of.. out of START_ARRAY token

I know there are a few questions on stackoverflow regarding this problem. But I have have been spending hours trying to resolve this error without any success.
I am using the mysql database to store the values.
I keep on getting the error message from the
com.example.springboot.Recipe file.
This is springboot recipe file
package com.example.springboot;
import com.example.springboot.Recipe;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Entity // This tells Hibernate to make a table out of this class
public class Recipe {
public Recipe(){
}
public Recipe(Integer id, String name, String description, String type, Integer preptime, Integer cooktime, String content, Integer difficulty){
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.preptime = preptimee;
this.cooktime = cooktime;
this.content = content;
this.difficulty = difficulty;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String description;
private String type;
private Integer preptime;
private Integer cooktime;
#Column(columnDefinition = "TEXT")
private String content;
private Integer difficulty;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return name;
}
public void setTitle(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getDifficulty() {
return difficulty;
}
public void setDifficulty(Integer difficulty) {
this.difficulty = difficulty;
}
public Integer getCookingtime() {
return cooktime;
}
public void setCookingtimeime(Integer cooktime) {
this.cooktime = cooktime;
}
public Integer getPreparationtime() {
return preptime;
}
public void setPreparationtime(Integer preptime) {
this.preptime = preptime;
}
}
Main Controller:
#PutMapping("/recipes/edit/{id}")
void updateRecipe2(#PathVariable int id, #RequestBody Recipe recipe ) {
Recipe recipe_ = recipeRepository.findById(id).get();
recipe_.setTitle(recipe.getTitle());
System.out.println("sss " + recipe.getname());
System.out.println("change");
recipeRepository.save(recipe_);
}
service.ts:
updateRecipe2 (id: number, recipe: any): Observable<any > {
const url = `${this.usersUrl}/edit/${id}`;
return this.http.put(url ,recipe);
}
where the updateRecipe2 gets called:
save(): void {
const id = +this.route.snapshot.paramMap.get('name');
this.recipeService.updateRecipe2(id, this.recipes)
.subscribe(() => this.gotoUserList());
}
as soon as the user clicks save this functions saves the changes made.
I hope the code snippets that I provided are enough to help solve the problem.
Thank you in advance.
I am building a rest api with spring boot and I am using angularjs as it's frontend. I am pretty new to web-development.
You are sending a list of recipes to an api endpoint that expects a single recipe object.
Your options are:
Send only one recipe object at a time, for example:
this.recipeService.updateRecipe2(id, this.recipes[0])
OR: create a new API endpoint to accept a list of recipes, to edit them in "batch"
#PutMapping("/recipes/edit")
void updateRecipes(#RequestBody List<Recipe> recipe ) {
my Example:
Use:
#PostMapping
Code:
public void setTransacciones(List<Transacciones> transacciones) {
this.transacciones = transacciones;
}
CodeBean:
public class Transacciones {
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
private String text;
}
Post(raw):
{
"transacciones" : [ {"text" : "1"}, {"text" : "2"} ]
}
Result:
{
"transacciones": [
{
"transaccionId": 2,
"text": "1"
},
{
"transaccionId": 3,
"text": "2"
}
]
}
BINGO!!

How to exclude value null from Put request when mapping to dto

I using RestController update data to db but I have problem. When i update value, if value from my update is null , it allways update data to db is null. I dont't want it. I want if 1 field with value is null from my request, i don't want update it.
This bellow my code :
Controller:
RestController
#RequestMapping("/api/products")
#Api(value = "ProductControllerApi",produces = MediaType.APPLICATION_JSON_VALUE)
public class ProductController {
#Autowired
private ProductService productService;
#PatchMapping("/{id}")
public ResponseEntity<ProductResDto> updateProduct(#RequestBody ProductReqDto productReqDto, #PathVariable String id) {
return ResponseEntity.ok(productService.updateProduct(product,id));
}
ProductReqDto:
public class ProductReqDto {
private String name;
private String type;
private String category;
private String description;
private Double prince;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
}
ProductResDto:
public class ProductResDto {
private String name;
private String type;
private String category;
private Double prince;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
}
MappingDto:
private ProductDto convertToProductDto(ProductReq product) {
return modelMapper.map(product, ProductResDto.class);
}
How to i handle method convertToProductDto only mapping with value not null. Because if , mapping one field : example : product_name = null , it insert to db null. I want if field ProductReq have value, it mapping and keep other different field in database(not set it null if not contain value from ProductReq) .
Example:
**ReqProductDto.class**
private String name;
private String type;
private String category;
private String description;
private Double prince;
but if user only update two field:
private String name;
private String type;
I want spring update field name, and field type user input and keep category,description,prince in my database. In my case, if user update two field: name, and field type,spring update it but spring set category,description,prince is null in my database. I don't want it.
Please help me, thanks.
You've tagged this as spring-boot, so I'm assuming you might be using controllers and validating their parameters. If that is the case, just do
import javax.validation.constraints.NotNull;
public class ProductReqDto {
#NotNull
private String name;
#NotNull
private String type;
#NotNull
private String category;
#NotNull
private String description;
#NotNull
private Double prince;
...
}
and use #Valid for your controllers like this
#PatchMapping("/{id}")
public ResponseEntity<ProductResDto> updateProduct(#RequestBody #Valid ProductReqDto productReqDto, #PathVariable String id) {
return ResponseEntity.ok(productService.updateProduct(product,id));
}
Then your object will be validated on instantiation.
What you want is mainly used for PATCH mapping.
IN a PUT mapping, all fields of an object need to override, but in a PATCH mapping only the fields which are provided needs to be overridden, others need not be changed.
So,
for an existing record,
employee{ employeeId = "A2RTD", empName = "satish", "country": "India"}
And, now one non-mandatory field mobileNo needs to be updated along with the country
DTO request will contain all field other than id, but only country & mobile no will not be null
In this scenario, we can use BeanUtils which is part of spring package
import org.springframework.beans.BeanUtils;
public static Object getDtoMapping(Object source, Object destination) {
BeanUtils.copyProperties(source, destination, getNullFieldNames(source));
return destination;
}
public static String[] getNullFieldNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> fieldNames = new HashSet<>();
for (PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null)
fieldNames.add(pd.getName());
}
String[] result = new String[fieldNames.size()];
return fieldNames.toArray(result);
}
Ths function "getNullFieldNames" will return fieldNames which have value null. So, those fields will not be mapped, as per 3rd optional paramter in BeanUtils
And, you need to pass
// PATCH
EmployeeDao emp = findById(empCode);
emp = (EmployeeDao) getDtoMapping(empUpdateDto, emp);
Here, in BeanUtil copyProperties, 3rd param is optional. If you give it works for PATCH mapping, if you don't give it behaves as PUT mapping.
Since, for PUT mapping, ignoring null as same as not ignoring.
You can use the same in POST, PUT mapping also.
// POST MAPPING
EmployeeDao emp = (EmployeeDao) getDtoMapping(empCreateDto, new Employee());

json and wrapper for gson

I am trying to get some the array of actors from Jira. The code for the wrapper is used in a Gson.fromJson call. I had used something similar with a json string that did not have an array in it that had the information I needed and it worked fine, so the issue seems to do with the array, but I am not 100% sure:
import com.google.gson.annotations.SerializedName;
public class JiraRoleJsonWrapper {
#SerializedName("self")
private String self;
#SerializedName("name")
private String name;
#SerializedName("id")
private int id;
#SerializedName("description")
private String description;
#SerializedName("actors")
private JiraActors[] actors;
public JiraActors[] getActors() {
return actors;
}
public void setActors(JiraActors[] actors) {
this.actors = actors;
}
public String getSelf() {
return self;
}
public void setSelf(String self) {
this.self = self;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String key) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/*
public String[] getAvatarUrls() {
return avatarUrls;
}
public void setAvatarUrls(String[] avatarUrls) {
this.avatarUrls = avatarUrls;
}
*/
}
class JiraActors {
#SerializedName("id")
private int id;
#SerializedName("displayNme")
private String displayName;
#SerializedName("type")
private String type;
#SerializedName("name")
private String name;
//#SerializedName("avatarUrl")
//private String avatarUrl;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The json it would receive:
{
"self":"http://someserver.com:8080/apps/jira/rest/api/2/project/10741/role/10002",
"name":"Administrators",
"id":10002,
"description":"A project role",
"actors":[
{
"id":12432,
"displayName":"Joe Smith",
"type":"atlassian-user-role-actor",
"name":"joesmi",
"avatarUrl":"/apps/jira/secure/useravatar?size=xsmall&ownerId=dawsmi&avatarId=12245"
},
{
"id":12612,
"displayName":"Smurfette Desdemona",
"type":"atlassian-user-role-actor",
"name":"smudes",
"avatarUrl":"/apps/jira/secure/useravatar?size=xsmall&ownerId=lamade&avatarId=10100"
},
This shows two actors and the format of the json. Please note I did not put a complete json response. It just shows two actors.
In my code, I tried the following to retrieve the actors:
InputStream is = response.getEntityInputStream();
Reader reader = new InputStreamReader(is);
Gson gson = new Gson();
JiraRoleJsonWrapper[] jiraRoleJsonWrapper = gson.fromJson(reader, JiraRoleJsonWrapper[].class);
for (JiraRoleJsonWrapper w : jiraRoleJsonWrapper) {
JiraActors[] a = w.getActors();
String name = a.getName();
It does not find getName for some reason. I am not sure why.
I figured it out.
I change the setActors to
public void setActors(ArrayList<JiraActors> actors) {
this.actors = actors;
}
Then I was able to get the array list and get access to the getName() method of JiraActors.

google endpoint message stops working after updating to java 7

Google endpoint message objects are very simple POJOs. I have a compound POJO that used to work but now is no longer working. The error I am getting, when android client makes the call, is that the JSON cannot be parsed because of AnimalTag. Here are the POJOs. To migrate to Java 7, I copied and pasted the code manually. So I thought that could have been the cause, that perhaps I left something out. But I can't think of what the problem may be. Other calls work fine. But this one keeps failing.
The usage is that the method receives Dog from client to save on server. Not all the data in Dog is filled, but many, including AnimalTag, are filled. Also, AnimalTag only has the manufacturer filled. Again This all used to work.
public class Dog {
private String name;
private String owner;
private AnimalTag tag;
public Dog(String name, String owner, AnimalTag tag) {
super();
this.name = name;
this.owner = owner;
this.tag = tag;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getOwner() {
return this.owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public AnimalTag getTag() {
return this.tag;
}
public void setTag(AnimalTag tag) {
this.tag = tag;
}
}
class AnimalTag{
private long number;
BlobKey imageKey;
String manufacturer;
public AnimalTag(long number, BlobKey imageKey, String manufacturer) {
super();
this.number = number;
this.imageKey = imageKey;
this.manufacturer = manufacturer;
}
public long getNumber() {
return this.number;
}
public void setNumber(long number) {
this.number = number;
}
public BlobKey getImageKey() {
return this.imageKey;
}
public void setImageKey(BlobKey imageKey) {
this.imageKey = imageKey;
}
public String getManufacturer() {
return this.manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
}
I got the answer, AnimalTag was missing the following constructor:
public AnimalTag(){}

java.util.Arrays$ArrayList cannot be cast to java.lang.String on hibernate restriction

I've been staring at this issue for too long. When executing the .list() line spring throws an exception with regards to the .add(Restrictions.in("LocationCode", locations))
java.util.Arrays$ArrayList cannot be cast to java.lang.String.
Restrictions.in is expecting (string, Object[]) no??
Can someone point out what I'm missing when passing in to the Restrictions.in??
public List getIDsByDivision(String SelectedAccountCode,
List SelectedDivision, String SelectedLocation) {
List IDs=null;
String result="--Select--";
Session session=null;
String[] locations=SelectedLocation.split(",");
try
{
session=sessionFactory.openSession();
if(null!=SelectedAccountCode && !SelectedAccountCode.equals("--Select--")
&&SelectedDivision.size()>0&&!SelectedDivision.equals("--Select--")
&&locations.length>0&&!SelectedLocation.equals("--Select--"))
{
IDs=session.createCriteria(Customer.class)
// TODO fix this to location
.setProjection(Projections.distinct(Projections.property("ID")))
.add(Restrictions.eq("AcctCode", SelectedAccountCode))
.add(Restrictions.eq("Division", SelectedDivision))
.add(Restrictions.in("LocationCode", locations ))
.list();
}
Customer class
package <removed>;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Customer {
#Id
private String ID;
#Column
private String Name;
private String AcctCode;
private String LocationCode;
private String Division;
private String ContactEmail;
public Customer(){}
public Customer(String iD, String name, String acctCode,
String locationCode, String division, String contactEmail) {
super();
ID = iD;
Name = name;
AcctCode = acctCode;
LocationCode = locationCode;
Division = division;
ContactEmail=contactEmail;
}
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getAcctCode() {
return AcctCode;
}
public void setAcctCode(String acctCode) {
AcctCode = acctCode;
}
public String getLocationCode() {
return LocationCode;
}
public void setLocationCode(String locationCode) {
LocationCode = locationCode;
}
public String getDivision() {
return Division;
}
public void setDivision(String division) {
Division = division;
}
public String getContactEmail() {
return ContactEmail;
}
public void setContactEmail(String contactEmail) {
ContactEmail = contactEmail;
}
}
Semi-false positive on what the issue was, the following works(I need to refactor, but hopefully the gist is understood):
String[] locations=SelectedLocation.split(",");
List<String> locs=new ArrayList<String>();
for(String l:locations)
{
locs.add(l);
}
...
The exception was actually referring to:
.add(Restrictions.in("Division", SelectedDivision))
(adding this for completeness)
.add(Restrictions.in("LocationCode", locs.toArray(new String[locs.size()]) ))

Categories

Resources