Spring WebClient Call Two Dependent API - java

I have two endpoints, the first is to http://localhost:8899/api/composition/appraisal, which will returns all performance appraisal data
[
{
"appraisalId": "ac234fbf-740c-4390-89d4-0e9753ad4d70",
"employeeId": "emp-4613",
"grade": "VERY_GOOD",
"score": 94,
"status": "NEW"
},
{
"appraisalId": "e9baf663-aa6e-4af8-ae16-ef50c886df28",
"employeeId": "emp-4623",
"grade": "VERY_GOOD",
"score": 94,
"status": "NEW"
}
]
I also have another endpoint to get bonus data http://localhost:8888/api/composition/bonus/{appraisal_id} which return bonus data based on appraisal id
{
"appraisalId": "ac234fbf-740c-4390-89d4-0e9753ad4d70",
"bonusAmount": 4760.0,
"bonusPaidDate": "2019-06-30",
"employeeId": "emp-4623",
"paidToBankAccount": "8240996587"
}
Giving the appraisal_id from first API as parameter, I must merge the result from both API calls into PerformanceAppraisalWithBonus.java class
public class PerformanceAppraisalWithBonus {
private UUID appraisalId;
private double bonusAmount;
private LocalDate bonusPaidDate;
private String employeeId;
private String grade;
private int score;
private String status;
getters / setters
}
How can I achieve this using Spring WebClient?

assuming that the second api http://localhost:8888/api/composition/bonus/{appraisal_id} replies 204 No content when it does not find or does not exist the id and in turn assuming that the expected response is a list of PerformanceAppraisalWithBonus (Flux)
then it would look like this:
webClient
#Component
public class TestClient {
public Flux<Appraisal> firstServiceList() {
return WebClient.create()
.get()
.uri("http://demo4307830.mockable.io/first")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToFlux(Appraisal.class);
}
public Mono<AppraisalBonus> firstServiceFindId(UUID uuid) {
return WebClient.create()
.get()
.uri("http://demo4307830.mockable.io/"+ uuid)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(AppraisalBonus.class);
}
}
Service
#Service
public class AppraisalServiceImpl {
#Autowired
private TestClient testClient;
public Flux<AppraisalCombine> combineTwo() {
return testClient.firstServiceList()
.flatMap(appraisal -> testClient.firstServiceFindId(appraisal.getAppraisalId())
.flatMap(appraisalBonus -> Mono.just(AppraisalCombine.builder()
.appraisalId(appraisalBonus.getAppraisalId())
.bonusAmount(appraisalBonus.getBonusAmount())
.bonusPaidDate(appraisalBonus.getBonusPaidDate())
.employeeId(appraisalBonus.getEmployeeId())
.grade(appraisal.getGrade())
.score(appraisal.getScore())
.status(appraisal.getStatus())
.build())));
}
}
Controller
#RestController
#RequestMapping("/appraisal")
public class AppraisalController {
#Autowired
AppraisalServiceImpl appraisalService;
#GetMapping(value = "/get")
public Flux<AppraisalCombine> uploadSimple() {
return appraisalService.combineTwo();
}
}
result
[
{
"appraisalId": "ac234fbf-740c-4390-89d4-0e9753ad4d70",
"bonusAmount": 4760.0,
"bonusPaidDate": "2019-06-30",
"employeeId": "emp-4623",
"grade": "VERY_GOOD",
"score": 94,
"status": "NEW"
}
]

Related

How to read GraphQL query result in Quarkus Application

I am very new to GraphQL and Quarkus. I have managed to run a quarkus application with GraphQL
following this link
I have the following classes:
Hero.java
package org.acme;
import java.util.*;
public class Hero {
public String name;
public String surname;
public Double height;
public Integer mass;
public Boolean darkSide;
public LightSaber lightSaber;
public static List<Integer> episodeIds = new ArrayList<>();
}
Film.java
package org.acme;
import java.time.LocalDate;
public class Film {
public String title;
public Integer episodeID;
public String director;
public LocalDate releaseDate;
}
Main file where I am defining the query:
package org.acme;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.microprofile.graphql.*;
import io.vertx.core.http.impl.ServerWebSocketImpl;
#GraphQLApi
public class FilmResources {
#Inject
GalaxyService service;
#Query("allFilms")
#Description("Get all Films from a galaxy far far away")
public List<Film> getAllFilms() {
return service.getAllFilms();
}
#Query("allHeroes")
#Description("Get all heroes name")
public List<Hero> getAllHeroes()
{
return service.getAllHeroes();
}
#Query("getFilm")
#Description("Get a film by it's id")
public Film getFilm(#Name("filmId") int id)
{
return service.getFilm(id);
}
// #Query("getFilmHeroes")
// #Description("Get heroes by film specified")
public List<Hero> heroes(#Source Film film) {
return service.getHeroesByFilm(film);
}
}
I run the following query on GraphQL Ui http://localhost:8080/q/graphql-ui:
query getFilmHeroes {
getFilm(filmId: 1) {
title
director
releaseDate
episodeID
heroes {
name
height
mass
darkSide
lightSaber
}
}
}
And I get the following json:
{
"data": {
"getFilm": {
"title": "The Empire Strikes Back",
"director": "George Lucas",
"releaseDate": "1980-05-21",
"episodeID": 5,
"heroes": [
{
"name": "Luke",
"height": 1.7,
"mass": 73,
"darkSide": false,
"lightSaber": "GREEN"
},
{
"name": "Leia",
"height": 1.5,
"mass": 51,
"darkSide": false,
"lightSaber": null
},
{
"name": "Darth",
"height": 1.9,
"mass": 89,
"darkSide": true,
"lightSaber": "RED"
}
]
}
}
}
My question where does the result of the query (this json file) is located? How can I read it in my program. Please refer me to some reading material or the steps I need to follow to read this query in my program.
GraphQL Query and result looks like this
Quarkus Setup looks like this
What you've been playing with is the server-side Quarkus extension for GraphQL. But Quarkus also has a client-side extension: quarkus-smallrye-graphql. That offers a library that can call remote (or local...) GraphQL servers and lets you work with the results programmatically. The guide for trying it is at https://quarkus.io/guides/smallrye-graphql-client - give it a try!

How to consume Json from rest template and map for each element to another Json in Spring Boot

I am not able to map a consumed json to another json to each element by using REST template in Spring Boot
Controller code:
public List<Getrequests> getallrequests() {
List<Getrequests> list=Serviceobj.allrequestdata();
return list;
}
Service code:
public List<Getrequests> allrequestdata() {
String urlGETList = "http://localhost:8082/myapp/userinfo/getusertype/asd454";//get by id call
ResponseEntity<Usertype[]> responseEntity =resttemplateobj.getForEntity(urlGETList, Usertype[].class);
Usertype[]objects = responseEntity.getBody();
List results = admininfoDaoobj.getallrequestsdata();
//results.add(objects);if i un comment this line of code i am getting 1 output means it just add to the list only
return results;
}
Dao code:
public List<Getrequests> getallrequestsdata(){
String hql = "FROM Createrequest";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
Query<Createrequest> query = getSession().createQuery(hql,Createrequest.class);
List resultlist= query.getResultList();
return resultlist;
}
Getrequests pojo class:
public class Getrequests{
private String userid;
private String username;
private String userphoneno;
.......getters and setters.....
}
User type pojo class:
public class Usertype{
private String usertype;
private String useraddress;
.......getters and setters.....
}
1 output:
[
{
"userid":"asd454",
"username":"satya",
"userphoneno":"1234567890"
},
{
"userid":"asd455",
"username":"satya",
"userphoneno":"1234567890"
}
[
{
"usertype":"admin,agent",
"useraddress:"dsadasd,asdasdsa"
},
{
"usertype":"agent",
"useraddress:"asdasdsa"
},
]
]
2 output
If I comment resultlist.add(objects) in Service code means it's not added to the getrequest list then I am getting below output:
[
{
"userid":"asd454",
"username":"satya",
"userphoneno":"1234567890"
},
{
"userid":"asd455",
"username":"satya",
"userphoneno":"1234567890"
}
]
But I need to map for each userid, I need to display the usertype and user address like below output.
3 Output
[
{
"userid":"asd454",
[
{
"usertype":"admin,agent",
"useraddress:"dsadasd,asdasdsa"
} ]
"username":"satya",
"userphoneno":"1234567890"
},
{
"userid":"asd455",
[
{
"usertype":"agent",
"useraddress:"asdasdsa"
}
]
"username":"satya",
"userphoneno":"1234567890"
}
]
I also tried the rest template call in Dao then also not getting.
How can I map for each userid to display the usertype and user address like as above shown 3 output.
From your snippets, it looks like you want to generate a specific POJO signature.
There are plenty of tools which can give you a stub out of a sample json request.
From your example, i would guess that changing the Getrequests would do the trick,
like :
public class Getrequests{
private String userid;
private String username;
private String userphoneno;
private Usertype userType;
.......getters and setters.....
}
You might need to change it to a list reference as well, depending on your schema and your overall requirement.
EDIT
public class Getrequests{
private String userid;
private String username;
private String userphoneno;
private List<Usertype> userType;
}
If i make the reference a list, then the below samples are the output :
Single object:
{
"userid":"69",
"username":"asd",
"userphoneno":"09876",
"userType":[
{
"usertype":"01",
"useraddress":"1234"
}
]
}
As An array :
[
{
"userid":"94",
"username":"asd",
"userphoneno":"09876",
"userType":[
{
"usertype":"01",
"useraddress":"1234"
}
]
},
{
"userid":"53",
"username":"asd",
"userphoneno":"09876",
"userType":[
{
"usertype":"01",
"useraddress":"1234"
}
]
}
]

How to Parse only a portion of a web JSON in Java using RestTemplate?

I'm trying to get the first 5 articles from this API: https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21
My code works perfectly for now, but it parses all 10 articles of NewsAPI.
The code is:
public News parse() {
return restTemplate.getForObject
("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", News.class);
}
}
and the result is:
{
"totalResults": 10,
"articles": [
{
"source": {
"id": "bbc-news",
"name": "BBC News"
},
"author": "BBC News",
"title": "Measles returns to four European nations, WHO says",
"url": "http://www.bbc.co.uk/news/health-49507253"
},
etc......
Of course, i created the classes that describe Article, Source and News. News has a List of Article.
I just want to parse the first five articles and save them into a List. I know I have to use a For cycle, but how can i do that? I tried with this code:
public News parseFive() {
List<Article> articleList = null;
for(int i = 0; i<5; i++) {
articleList = Arrays.asList(
new Article(restTemplate.getForObject
("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Article.class)));
}
News news = new News();
news.setArticles(articleList);
return news;
}
The News class is:
public class News {
private int totalResults;
private List<Article> articles;
public News() {
}
public int getTotalResults() {
return totalResults;
}
public void setTotalResults(int totalResults) {
this.totalResults = totalResults;
}
public List<Article> getArticles() {
return articles;
}
public void setArticles() {
this.articles = articles;
}
}
and the result is:
{
"totalResults": 0,
"articles": [
{
"source": null,
"author": null,
"title": null,
"url": null
}
]
}
Where is the problem? Maybe because the first class who finds is not Article but is News? Thanks everyone for the effort.
When you are using RestTemplate.getForObject you are technically parsing the whole response: Spring reads all the bytes and uses JSON parser (Jackson) to create an object. Your for loop, which is covered later, only filters out elements past 5th. If you really want to parse only first 5 articles, you should consider using Jackson Streaming API. It is quiet problematically to use with RestTemplate, read this answer for more info.
Now let's try to fix your parseFive.
First, create a class to capture whole response:
public class Response {
private String status;
private Integer totalResults;
private List<Artice> articles;
// Getters & Setters
}
Now, get first five articles:
public News parseFive() {
final Response response = restTemplate
.getForObject("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Response.class);
final News news = new News();
news.setArticles(response.articles.stream().limit(5).collect(Collectors.toList());
return news;
}
You have not provided your News class, probably it is the same as response. Then, the code may look like:
public News parseFive() {
final News news = restTemplate
.getForObject("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Response.class);
news.setArticles(news.articles.stream().limit(5).collect(Collectors.toList());
return news;
}

Spring Cloud Wiremock doesn't pick transformers from JSON file during tests

With the following setup, spring doesn't pick a transformer defined in JSON file which is under mappings directory, but it does when I declare it directly in test code.
#Configuration
public class WiremockConfiguration {
#Bean
WireMockConfigurationCustomizer optionsCustomizer() {
return new WireMockConfigurationCustomizer() {
#Override
public void customize(WireMockConfiguration options) {
options.extensions(BodyDefinitionTransformer.class);
}
};
}
}
{
"request": {
"method": "POST",
"urlPattern": "/some/thing"
},
"response": {
"status": 200,
"bodyFileName": "my_payload.json",
"transformers": [
"body-transformer"
],
"headers": {
"Content-Type": "application/json"
}
}
}
public class BodyDefinitionTransformer extends ResponseDefinitionTransformer {
#Override
public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files,
Parameters parameters) {
return responseDefinition; //checking if this work by putting breakpoint here
}
#Override
public boolean applyGlobally() {
return false;
}
#Override
public String getName() {
return "body-transformer";
}
}
#ContextConfiguration
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource
#AutoConfigureWireMock(port = 9632)
class DummyTestClass extends Specification {
def "some dummy test" () {
when:
stubFor(post("/some/thing").willReturn(aResponse()
//.withTransformers("body-transformer") transformer work if I declare it in this way
.withTransformerParameter("test", "test"
)))
// rest of my test where execute above request
}
}
The code works perfectly when I declare it using .withTransformers("body-transformer") but when I put transformer name into transformers array in JSON file, it doesn't work. Do you have any ideas why?

How to parse complex JSON with Retrofit 2 in Android Studio

I am trying to get the stopId from this API but I am having a hard time parsing it using retrofit 2 + gson. I've only got experience with less complicated JSON API's. Could anyone help me?
{
"direction": "inbound",
"timetable": {
"$type": "Tfl.Api.Presentation.Entities.Timetable, Tfl.Api.Presentation.Entities",
"departureStopId": "940GZZCRECR",
"routes": [{
"$type": "Tfl.Api.Presentation.Entities.TimetableRoute, Tfl.Api.Presentation.Entities",
"stationIntervals": [{
"$type": "Tfl.Api.Presentation.Entities.StationInterval, Tfl.Api.Presentation.Entities",
"id": "0",
"intervals": [{
"$type": "Tfl.Api.Presentation.Entities.Interval, Tfl.Api.Presentation.Entities",
"stopId": "940GZZCRLEB",
"timeToArrival": 2
}, {
"$type": "Tfl.Api.Presentation.Entities.Interval, Tfl.Api.Presentation.Entities",
"stopId": "940GZZCRSAN",
"timeToArrival": 3
}]
}, {
}, {
}],
"schedules": [
]
}]
}
}
Create your models automatically with this tool. Just paste an example json response.
http://pojo.sodhanalibrary.com
Remember to check and edit types of your variables, sometimes they can be null. After that make your call as usual.
You have to create proper models hierarchy, for example:
BaseModel:
public class BaseModel {
String direction;
Timetable timetable;
}
Timetable:
public class Timetable {
String $type;
String departureStopId;
List<Route> routes;
}
Route:
public class Route {
String $type;
List<StationInterval> stationIntervals;
List<Integer> schedules;
}
StationInterval:
public class StationInterval {
String $type;
int id;
List<Interval> intervals;
}
Interval:
public class Interval {
String $type;
String stopId;
int timeToArrival;
}
And make retrofit call as usual:
#GET("some_url")
Call<BaseModel> loadSomeData();
A simple and efficient way of generating POJO from JSON is http://www.jsonschema2pojo.org/
After you have included the models generated from the above link, you can continue reading this if you need some info setting up Retrofit 2.0.
Now, you would have to define a interface for the APIs
public interface MyAPI {
#GET("/url")
Call<ResponseModel> getData();
}
Then create a class to get the retrofit client
public class MyDataClient {
public static final String BASE_URL = "";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging);
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
}
return retrofit;
}
}
Then when you need to call the API do this,
MyAPI apiService =MyDataClient.getClient().create(MyAPI.class);
Call<ResponseModel> call = apiService.getData();
call.enqueue(new Callback<ResponseModel>() {
#Override
public void onResponse(Call<ResponseModel> call, Response<ResponseModel> response) {
}
#Override
public void onFailure(Call<ResponseModel> call, Throwable t){
}
});

Categories