How to call rest API in the loop - java

I want to call the third party API multiple times using the RestTemplate(for each customer id I have to call REST API) currently I have written like below and its working fine but it's taking time because there are many customers I'd and calling API for each customer id, is there any way I can make this parallel.
public List<Organization> getCustomeOrganizationInfo(){
String url="https://url.net/core/v1/customers"
List<Organization> organizationList = new ArrayList<>();
for(Customer customer:CustomerList){
String restUrlWithUserId=url+"/customer.getCustomerId"
CustomerInfo customerInfo = restTemplate.exchange(
restUrlWithUserId,
HttpMethod.GET,
request,
String.class
);
Organization organization =new Organization();
organization.setCustomerId(customer.getCustomerId())
organization.setorganizationId(customerInfo.getCustomeOrganizationId())
organization.setorganizationname(customerInfo.getCustomeOrganizationName())
organizationList.add(organization)
}
}

Is there any way I can make this parallel
For concurrency and clean code, you should separate your restTemplate call to another class(service), for example, ThirdPartyCustomerService.java. This class will be held responsible for calling outside.
#Service
public class ThirdPartyCustomerService {
private final RestTemplate restTemplate;
private final String url = '...';
...
public CustomerInfo getCustomerInfo() {
return this.restTemplate...
}
}
Then you can inject this class into your service class. Now if you want to run it concurrency. You could try #Async and Future here. Just need a little bit of change on the new service and remember to call Future.get() on your main service.
#Async
public Future<CustomerInfo> getCustomerInfo() {
return new AsyncResult<CustomerInfo>(this.restTemplate...);
}
Or you can use WebClient, an alternative for RestTemplate and AsyncRestTemplate.

Related

How to test with Postman a controller's method that has one or more objects parameters (and not simple params) in Spring?

I am a newbie in Spring development. I need to create a simple application, a controller that has a method that takes as parameter an object of a custom designed entity class into the project. The prototype looks like this:
#RestController
public class JobsController {
#PostMapping("/search")
public ResponseEntity<?> search() {
log.info("JobsController -> search method");
//JobSearchEntity jobSearchEntity = modelMapper.map(jobSearch, JobSearchEntity.class);
List<JobEntity> jobs = jobService.searchJobs();
//log.info(String.format("Job found: %s ", jobSearch));
return ResponseEntity.ok(jobs);
}
}
Can someone who is more advanced into this staff with Postman testing tell me how to do that , how to test a controller method which takes parameters?
You can use postman to submit parameters in JSON format after adding # requestbody annotation on the method, or submit parameters directly in form without annotation
You can use this example. Is very simple exemple.
#RestController
#RequestMapping("/root")
public class RootController {
private final RootService service;
public RootController(final RootService service) {
this.service = service;
}
#PostMapping("/exemple")
public void createRoot(#RequestBody final RootDto dto) {
service.createRoot(dto);
}
}
Then you can send request to POST host/root/exemple with your JSON.
More exampls you can find here: https://www.baeldung.com/spring-request-response-body
It seems you are missing an honest search on google about the subject.
You can make use of #RequestBody annotation to accept method arguments.
Check these page for examples --
#RequestBody and #ResponseBody annotations in Spring
https://stackabuse.com/get-http-post-body-in-spring/
https://www.twilio.com/blog/create-rest-apis-java-spring-boot
These set of playlist on youtube are very good starter course for SpringBoot -
https://www.youtube.com/c/JavaBrainsChannel/playlists
Postman Tutorial--
https://www.youtube.com/watch?v=VywxIQ2ZXw4
To get data from api is preferred to use GET method :
#RestController
public class JobsController {
#GetMapping("/search")
public ResponseEntity<?> search(#RequestParam("id") String id,#RequestParam("desc") String desc) {
log.info("JobsController -> search method");
//JobSearchEntity jobSearchEntity = modelMapper.map(jobSearch, JobSearchEntity.class);
List<JobEntity> jobs = jobService.searchJobs();
//log.info(String.format("Job found: %s ", jobSearch));
return ResponseEntity.ok(jobs);
}
}
you call this api with post man this way :
#PostMapping used usually to save new data (example : create job )
Take look on rest resource naming guide

Mocking nested retrofit api calls using MockWebServer

I am writing a junit test using okhttp3.mockwebserver for a retrofit2 rest api.
The trimmed down api looks like this:
public interface MyApi{
#POST("/api/get-orders")
retrofit2.Response<Set<String>> getOrders();
#POST("/api/cxl-order")
retrofit2.Response<String> cancelOrder(String ordeId);
}
The api is then injected to another class which delegates the calls thusly:
public class MyExchange{
private final MyApi api;
public MyExchange(MyApi api){
this.api = api;
}
public final Set<String> getOrders(){
Response<Set<String>> resp = api.getOrders();
//parse the response
Set<String> result = parse( resp );
return result;
}
public final boolean cancelOrder( String orderId ){
api.cancelOrder( orderId );
//Nested Call
Set<String> orders = getOrders();
return !orders.contains(orderId);
}
}
I do the following in my test:
#Test
public void cancel_order(){
MockWebServer server = new MockWebServer();
server.start();
String orderId ="OrderId_123";
MyApi mockApi = new Retrofit.Builder().baseUrl("/").build().create(MyApi.class);
MyExchange exchange = new MyExchange(mockApi);
server.enqueue( new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK, orderId));
server.enqueue( new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK, Set.of()));
exchange.cancelOrder(orderId);
}
Because the implementation of cancelOrder() calls api.cancelOrder() and then api.getOrders(), I added two mocked responses corresponding to each. However, looks like only the first mocked responses gets returned. For the second (getOrders), the mock server actually tries to connect over REST and then fails by timing out.
Any ideas as to how to mock responses for nested calls?
Cheers!
I ended up using the Dispatcher to check the path of the request.
If the path ends in "get-orders", I send mocked response for Orders otherwise for cancel orders.
Dispatcher dispatcher = (request) -> {
if( request.getPath().endsWith("get-orders"){
return mock response for orders
}else if( request.getPath().endsWith("cxl-orders"){
return mock response for cancel orders
}
}
mockServer.setDispatcher(dispatcher);

How to make a REST API call inside subscribe method of a Mono<T>

I have a Spring boot application with Spring WebFlux. I want to call a API Rest in a non blocking way and after that, inside subscribe method, call another API Rest.
The first call executes correct and the subscribe method too. My problem is that inside the subscribe, how can I request another API Rest?
#Autowired
WebClient.Builder webClientBuilder;
Mono<UserRating> monoUserRating = webClientBuilder.build().get().uri("http://ratings-data-service:8083/ratingsdata/user/" + userId) .retrieve().bodyToMono(UserRating.class);
monoUserRating.subscribe(CatalogResource::handleResponseUserRating);
private static List<CatalogItem> handleResponseUserRating(UserRating userRating) {
System.out.println("How to call another API???");
//this doesn't work since is not static
Movie movie = webClientBuilder.build().get().uri("http://movie-info-service:8082/movies/"+ rating.getMovieId())
.retrieve().bodyToMono(Movie.class).block();
}
Use Mono.flatMap to bind two async operations together, link to doc.
#Autowired
WebClient.Builder webClientBuilder;
public void main() {
Mono<Movie> movieMono = getUserRating(userId)
.flatMap(userRating -> handlerUserRating(userRating));
movieMono.subscribe(movie -> handleMovie(movie));
}
private Mono<UserRating> getUserRating(String userId) {
return webClientBuilder.build()
.get()
.uri("http://ratings-data-service:8083/ratingsdata/user/" + userId)
.retrieve()
.bodyToMono(UserRating.class);
}
private Mono<Movie> handleUserRating(UserRating rating) {
return webClientBuilder.build()
.get()
.uri("http://movie-info-service:8082/movies/"+ rating.getMovieId())
.retrieve()
.bodyToMono(Movie.class);
}

How to invoke Restful WebService in Spring Boot

I have a SB service that is being used to send email. I wanted to use that in my existing application , how can I do that? I am thinking to create a controller that handles incoming HttpRequest and HttpResponse. But still no idea on how my existing application will invoke it. I need some high level overview too on how exactly SB application will run independently with other application.
P.S.- there is no UI interface for the email service so i wont be mapping url like we do in controllers generally.
Here is my sample email service:
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
I created a controller like this earlier to test this out:
#RestController
public class CourseController {
#Autowired
private EmailService emailService;
#RequestMapping(value = "/sendEmail", method = RequestMethod.POST)
public void sendEmail() {
emailService.sendMessage("abc#gmail.com","{client#gmail.com}", "testSubject",new Context);
}
Context has some business data.
I have a jsp that I am using and posting my form through which it is mapping. It all works fine.
But now I want to integrate this with my existing application (its on struts 1)so there wont be any uri to map. There must be some kind of HttpRequest need to be created from the invoking application and my controller should be handling it. How can I achieve this?
You have already this service implemented? Then you need a RestController class that mapps the uri of your choice. In this class you need to inject the service class that realizes your email sending method. Is this class annotated with #Service? Quite difficult to explain without seeing your code. Here an example for a REST-Interface:
#RestController
#RequestMapping("/api/v1/email")
public class RestClass {
private EmailService emailService;
#Autowired
public RestClass(EmailService emailService){
this.emailService = emailService;
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> sendEmail(#RequestBody EmailDTO emailDTO){
String emailAdress = emailDTO.getEmail();
this.emailService.sendEmail(emailAdress);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
So in this case the emailService would be the class that has the method that sends your email. This class should be annotated with #Service. Hope that helps.
Here your existing class:
#Service
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
And in case the injection doesn't work you have to annotate your application class with #ComponentScan({"com.foo.dal.","com.foo.notification."}) replace this packages simply with the package of your service and resource class.
I am not sure about the problem. If I am right that you need to call a rest service from your application. In this case it is lot easier and convenient to use Spring's RestTemplate link
You can get some overview here

Spring MVC requests processing order

I have a web service that receives some data from clients and saves them to a Map
#Controller
public class ExampleController {
private final Map<String, String> state = new ConcurrentHashMap<>();
#RequestMapping(value = "/set-state", method = RequestMethod.POST)
public synchronized #ResponseBody void setState(#RequestParam String id,
#RequestBody String updatedData) {
state.put(id, updatedData);
}
}
Suppose that a client sends two requests one after another. Both requests have the same id, but the second request has updated data.
The question is, does Spring MVC guarantee that setState will be called with request 1 data first? Is making the method synchronized enough to make sure I'll always get the updated data from request 2 in the state Map?

Categories