Spring Data - Using property values in projections - spring

I have Spring Data Rest Application where I created a projection like this
#Projection(name = "UserWithAvatar", types = { User.class })
public interface UserWithAvatar {
String getName();
String getEmail();
#Value("${app.url}/pictures/#{target.imageId}")
String getAvatar();
}
The non working part is the getAvatar, it generates a url for seeing the picture.
However this ${app.url} is not working inside the projection.
How would I add this application.properties value in there?

Use #environment.getProperty('app.url') inside #{} block. It works on Spring Boot 2.3.0, i'm not sure about older versions.
Example:
#Value("#{target.pictureUrl ?: #environment.getProperty('app.url') + 'static/default-avatar.png'}")
String getAvatarUrl();

Related

Given an assignment to return specific data using Springboot reactive but the JSON is really complicated

I am new to Springboot reactive
I was asked to call the following endpoint and return todays weather data only:
https://api.weather.gov/gridpoints/MLB/33,70/forecast
I believe I need to use something like this...
WebClient.create().get()
.uri("https://api.weather.gov/gridpoints/MLB/33,70/forecast")
.retrieve()
.bodyToMono(WeatherClass.class)
.block();
Do I need to map out an entire java object to match the JSON at the endpoint? is there an easy way to perhaps just grab the a certain piece of the JSON?
How would I handle something like the #context annotation in the JSON.
The WebClient in spring boot automatically uses Jackson's ObjectMapper to unmarshall json to a java object when the content type of the response is application/json. So there is no need to pull in any additional libraries or have to write any specific unmarshalling code, unless you want to use an alternate json-to-java library.
When using Jackson, you don't need to map every field in the json to your java object. You can annotate your java class with #JsonIgnoreProperties to inform jackson to ignore any properties that may appear in the json but do not have a matching field in your java object.
An example WeatherClass in which you want only the #context and forecastGenerator unmarshalled would look something like this
#JsonIgnoreProperties
public class WeatherClass {
private final List<Object> context;
private final WeatherProperties weatherProperties;
public WeatherClass(#JsonProperty("#context") List<Object> context,
#JsonProperty("properties") WeatherProperties weatherProperties) {
this.context = context;
this.weatherProperties = weatherProperties;
}
private class WeatherProperties {
private final String forecastGenerator;
private WeatherProperties(#JsonProperty("forecastGenerator") String forecastGenerator) {
this.forecastGenerator = forecastGenerator;
}
}
}
Note
#context seems to be an array that can contain multiple types (both objects and strings in your example). I've used Object to work around this but obviously isn't the most graceful solution but should be adequate to demonstrate how Jackson works
Alternatively, you can unmarshall the response to a JsonNode, which you can then use to traverse the structure of the json without converting it to a java object. For example
String forecastGenerator = WebClient.create().get()
.uri("https://api.weather.gov/gridpoints/MLB/33,70/forecast")
.retrieve()
.bodyToMono(JsonNode.class)
.block().get("properties").get("forecastGenerator").toString()
There are many other annotations provided by Jackson that can used to define how the unmarshaller functions. Too many to cover here. See Jackson Deserialisation Annotations

How to auto generate response fields that do not have POJO

We have a service that simply returns the json document on a GET request. Since we do not have the POJO for the response "model", it appears we won't be able to use the auto response fields generation "goodness".
One option for us is to create the Pojos (quite large, about 50 attributes) and a corresponding controller that uses the pojos. This is awkward as we now have to maintain the model and corresponding controller just so we can auto generate the model.
Any ideas on how we can still leverage some auto generation of the response fields would be greatly appreciated.
Here's the controller I'm referring to:
#RestController
#RequestMapping("/api")
public class ProductController {
#Autowired
ProductService productService;
#RequestMapping(value = { "/products/{ids}" }, method = { RequestMethod.GET },
produces = "application/json", headers={"accept=application/json"})
#Timed
#ExceptionMetered
#LogExecutionTime
public String getProductDetails(#PathVariable("id") String id) {
return productService.getProductDetails(id);
}
At the moment I see no way of leveraging the auto generation without putting additional effort into it. Spring Auto REST Docs works by inspecting POJOs with a Jackson visitor (static introspection without runtime information) and there is currently no way of deriving the JSON fields from a string (would be dynamic at runtime). Thus, I only see two options:
The approach that you already described: Creating the corresponding POJO and using it.
Using Spring REST Docs for the corresponding test and manually document each field in the test. Might be the better option here if you do not want to alter the production code.

How concatenate two string in Spring Expression Language (SpEL)

In my spring application, the methods from my controller and service classes have this annotation to security purposes:
#PreAuthorize("hasPermission(#user, 'cadastra')")
the second argument, the permission, should have this format:
<<action_name>>_<<class_name>>
What expression I should use to accomplish that, taking in consideration the class name is held by this.getClass().getName()?
To concatenate two strings in Spring EL you use concat function . See here for more details :
Spring EL docs
for example, I used the following :
#PreAuthorize("hasRole('ROLE_'.concat(this.class.simpleName))")
I finally solve this. I add a new method in my controller:
public String getName() {
String nome_classe = entityClass.getSimpleName();
System.out.println("getName nome_class = "+nome_classe);
return nome_classe;
}
and now I use the annotation in that way:
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")

How to get Spring MVC #PathVariable without using an annotation?

Given the spring mvc method like the one below.
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(#PathVariable("ownerId", int ownerId,
#PathVariable("petId") int petid) {
}
Is there some way to write the method so that all the URI templates variables are passed in as a
map to the handler? something along the lines of ?
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(Map<String,Object> allPathVariables) {
Integer ownerId = allPathVariables.get("ownerId");
Integer petId = allPathVariables.get("petId");
}
Is there a way to put all the URI templates in a Map that is passed to a handler method?
Looks like this can't be done with Spring MVC 3.1 there is an issue for it on the spring JIRA which is marked fixed for Spring MVC 3.2 https://jira.springsource.org/browse/SPR-9289

Spring JDO - makePersistent not returning created object

I'm using Spring and JDO connecting to a MySQL database.
When I persist an object, I am expecting to see the created object returned by the makePersistent() method. It does return an object, but this object only has the ID of the newly created object. All the other fields that were persisted now have a value of null.
In the code example below, I insert a value of 12 with the carouselNumber. The returned object has that value set to NULL and the id has the newly created ID value from the database.
I've used JDOHelper.getObjectState() and found that my object is in a Transient state.
I'm wondering if there is some annotation that I am missing to tell JDO to return all the values on the object, rather than just the newly generated ID.
EDIT:
I've done some further exploring and found that when I use the raw Datanucleus JDP API that thsi works fine. The problem only seems to be when I use Spring's JDO template. I'd really like to get an understanding of why this differs. Thanks
Thanks in Advance,
Brian.
#PersistenceCapable(table = "CAROUSEL", identityType = IdentityType.APPLICATION)
public class Carousel {
#PrimaryKey(column = "ID")
#Persistent(valueStrategy = IdGeneratorStrategy.INCREMENT)
private Long id;
#Column(name = "CAROUSEL_NUM")
private int carourselNumber;
......
public class CarouselDAOImpl extends JdoDaoSupport implements ICarouselDAO {
public Carousel insert(Carousel carousel) {
return getJdoTemplate().makePersistent(carousel);
}
.....
The makePersistent method alters the carousel object you've passed in to generate an ID, so it doesn't need to return anything. Just make your method void and use the carousel you've passed in.

Resources