What is the best way to return from controller object with two flux(arrays) inside?
I have two reactive repositories. One with football clubs, another one with countries. I want to return dto like:
public record InitData(Flux<FootballClub> footballclubs, Flux<Country> countries){}
So I can return list instead of flux via .block but this is not good idea. How can I subscribe two repositories in controller and return two arrays?
You can't return multiple Flux instances inside a DTO. Flux need to be subscribed somewhere in order to be useful. Assuming you have the following response DTO:
public class Response {
private List<String> footballClubs;
private List<Integer> countries;
}
You can use .collectList() method to collect all entities emitted by your repository into a List:
private Mono<List<String>> getFootballclubs() {
return footballclubsRepository.select().collectList();
}
private Mono<List<Integer>> getCountries() {
return countriesRepository.select().collectList()
}
And finally, map the resulting Monos into the response DTO:
Mono.zip(getFootballclubs(), getCountries())
.map(tuple2 -> Response.builder().footballClubs(tuple2.getT1()).countries(tuple2.getT2()).build())
Note that this is a valid solution in case we have a limited number of clubs and countries.
Related
Right now, I have an #Entity say Car that has a certain set of attributes. This gets persisted into a database.
Now, in the #RestController, if I want to accept a Car parameter except for certain properties, how do I do that? Right now, I am creating a different class called CarInput that is the same as Car minus those properties.
Again, for REST API response, same thing. If I want to return a Car but with a certain field removed. Right now I created CarResponse as a model.
Is there a cleaner way to do this?
I'd make the case that your external representation and your internal storage should hardly ever be identical. Sure, there'll be significant overlap, but your database and your API should be as independent from each other as possible.
I'd say it's a good practice to have separate domain models for the model and view layer (read: two different Car classes, in different packages). You can use a mapping framework like Dozer or MapStruct to map back and forth between these different entity types.
There are two common approaches to such problem.
Use #JsonIgnore on fields/getters that you want to exclude. However, this can lead to annotation hell or generally hard to read code.
#JsonIgnore
private String password;
Create a DTO class that data would be deserialized from or serialized to. What I mean is that when some user makes a POST request with a car definition, it would be deserialized by spring to CarDto and then parsed by you in the service layer to the Car object which you could save to a database. Similarly, Car object would be parsed to CarDto if the user asks for a data.
#GetMapping("/{userId}")
UserDto getUser(#PathVariable Long userId) {
return userService.getUser(userId);
}
#PostMapping
UserDto addUser(#RequestBody UserDto userDto) {
return userService.createUser(userDto);
}
This one, on the other hand, could lead to a situation where you sometimes use a Dto and sometimes the class itself. Because of that, consider parsing to/from CarDto only in the controller layer (unlike in the example above)
Also it's good to avoid creating two classes in one file. It makes hard to find a desired class afterwards.
You can still avoid of using a DTO class.
When you post Car object to controller your can control the wanted properties and operate on it.
For selecting fields to return as the response you can use json views.
Entity :
public Car {
private String color;
#JsonView(Views.Public.class)
private Integer weight;
// getters, setters
}
Controller :
#RestController
public CarController
#Autowired
private CarRepository carRepository;
#GetMapping("/{id}")
#JsonView(View.Public.class)
public Book get(#PathVariable Long id){
return carRepository.findOne(id);
}
#PostMapping
public Book update(#RequestBody Car car) {
// only properties we want to update
if(car.getColor() != null) {
// save in database or other operations
}
}
}
View :
public class Views {
public static class Public {
}
}
This way the controller's method "get" will send to client only "weight" property and "update" method will operate only on selected properties.
I am new to functional paradigm, wondering how to go about doing some querying before creating a new object?
#Override
public Mono<Order> create(CreateOrderRequest specs) {
//itemRepository.findAll(specs.getItemCodes()) //returns Flux<Item>
final Order newOrder = new Order(items);
return orderRepository.insert(newOrder)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to create order")));
}
How do I chain the commented code in a non blocking way? The query returns Flux<Item> while Order constructor requires a List<Item>
You can use the collectList() method, which will change your Flux<Item> into a Mono<List<Item>>.
After that, you can use the map() method to convert your List<Item> into an Order object, and the flatMap() method to get the saved result.
For example:
return itemRepository
.findAll(specs.getItemCodes())
.collectList()
.map(Order::new)
.flatMap(orderRepository::insert)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to create order")));
I have successfully created a springboot app that returns all the basic endpoints. Now I want to return just few fields from that endpoint in my request. For instance, return status from /health page to my rest call. How do I filter this or make my rest call more specific?
The actual requirement is two return few fields from /env, /health of different apps in one call. For which I am able to do it by returning all fields for both env and health. I just need to return specific fields from them. Also can I use in memory json objects, if so how should I do it?
Finally I figured out as how to create it. So the incoming json object consists of fields in LinkedHashMap type. So I consumed its field values using key
LinkedHashMap response = (LinkedHashMap)restTemplate.getForObject("http://localhost:8080/env",Object.class);
EnvProperties variables = new EnvProperties (response);
Wrapper POJO for all fields,
public EnvProperties (LinkedHashMap body) {
this.sysProperties = new SysEnvProperties((LinkedHashMap) body.get("systemProperties"));
}
POJO for this field,
public SysEnvProperties(LinkedHashMap body) {
this.javaVersion = body.get("java.version").toString();
}
later creating a new json string
#Override
public String toString() {
String s = null;
try {
s = mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return s;
}
I repeated the same for fields of interest, creating a POJO for each. Finally called these fields using similar wrapper class whose toString method returned the expected json object of desired fields only.
You can create Custom health endpoint or custom heath checker too.
For e.g.
#Component
public class CustomHealthCheck extends AbstractHealthIndicator {
#Override
protected void doHealthCheck(Health.Builder bldr) throws Exception {
// TODO implement some check
boolean running = true;
if (running) {
bldr.up();
} else {
bldr.down();
}
}
}
For further reading:
http://www.christianmenz.ch/programmieren/spring-boot-health-checks/
http://briansjavablog.blogspot.be/2017/09/health-checks-metric-s-more-with-spring.html
http://www.baeldung.com/spring-boot-actuators
You can find a tutorial here. However, the interfaces you want to look into implementing are:
org.springframework.boot.actuate.endpoint.Endpoint
Similar to creating a Controller. This is your /custom-health endpoint.
org.springframework.boot.actuate.metrics.CounterService
You can count integer-value metrics which will be available at /metrics.
org.springframework.boot.actuate.metrics.GaugeService
Or, you can measure double-value metrics which will be available at /metrics.
org.springframework.boot.actuate.health.HealthIndicator
Add metrics to the /health endpoint.
I have a domain with uml diagram
here.
I have declared JpaRepositoryes for classes: Invoice, Contract, Consultant, User, Region
I don't want to expose the repositories as they are because I will need to wrap some business rules around them.
I have defined a few projections and they seem to work with my repositories yet they do not kick in with my #RestController
Here is a draft of my wrapper:
#RestController
#RequestMapping("/contractService")
public class ContractServiceImpl extends ServiceImpl<Contract, Long> implements IService<Contract, Long>
{
#RequestMapping("/all")
public List<Contract> all()
{
return findAll();
}
#RequestMapping(value = "/one", method = GET)
public Contract one(#RequestParam(value = "id", defaultValue = "1") Long id)
{
log.info(format("id=%d", id));
return repository.getOne(id);
}
#Autowired
RepositoryRestConfiguration rrc;
#RequestMapping("config")
public List<String> getConfig()
{
return rrc.projectionConfiguration()
.getProjectionsFor(Contract.class)
.entrySet().stream()
.map(e -> e.getKey() + "->" + e.getValue())
.collect(Collectors.toList());
}
Here the last method is to verify if projections got configured. i.e.:
http://localhost:8080/invoiceapi/contractService/config
gets me the result as:
[
"brief->interface e.invoice.entity.projection.Brief",
"contractEdit->interface e.invoice.entity.projection.ContractEdit"
]
My projection is defined as:
#Projection(name="brief", types={Contract.class, Consultant.class, User.class, Region.class})
public interface Brief
{
public Long getId();
public String getName();
}
Rest calls into repository directly returns me the projected results as desired, id and name only:
http://localhost:8080/invoiceapi/contracts?projection=brief
http://localhost:8080/invoiceapi/contracts/1?projection=brief
However those into my controller:
http://localhost:8080/invoiceapi/contractService/all?projection=brief
http://localhost:8080/invoiceapi/contractService/one?id=2&projection=brief
they recursively spider all reachable entities as far as the defined JpaRepositories go. (The Application objects below User do not show up)
My controller returns a document with Content-Type: application/json while JpaRepository based one returns a cool looking Content-Type: application/hal+json. Their output are different as well: My controller returns a more straightforward output while JpaRepository based one puts associated objects into an array called _embedded.
What are the pros and cons of using
#GET
#Path("....
#Produces(MediaType.APPLICATION_JSON)
public Response objects (...
return Response.status(HttpServletResponse.SC_OK).entity(objects).build();
and
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Test> getTestsBrowser() {
List<Test> tests = new ArrayList<Test>();
tests.addAll(TestDao.instance.getModel().values());
return tests;
}
Does one of the methods is better in terms of the flexibility for the json model (meaning returning more complex model for example two types of objects that have reference to each other). Also what if the list is empty or null?