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!
Related
I'm reading spring in action 5th and learning spring-cloud, hateoas and webflux.I tried to write a rest controller as following
import static org.springframework.hateoas.server.reactive.WebFluxLinkBuilder.*;
#RestController
#RequestMapping(path = "/")
public class ServiceController {
private IngredientServiceClient ingredientClient;
#Autowired
public ServiceController(IngredientServiceClient ingredientClient) {
this.ingredientClient = ingredientClient;
}
#GetMapping("/ingredients/{id}")
public Mono<EntityModel<Ingredient>> getIngredientById(#PathVariable("id") String id) {
return ingredientClient.getIngredientById(id)
.flatMap(ingredient -> {
EntityModel<Ingredient> model = EntityModel.of(ingredient);
Mono<Link> link = linkTo(methodOn(ServiceController.class).getIngredientById(id)).withSelfRel().toMono();
return link.map(lk -> model.add(lk));
});
}
}
IngredientServiceClient.getIngredientById
public Mono<Ingredient> getIngredientById(String id) {
return wcBuilder.build()
.get().uri("http://ingredient-api/ingredients/{id}", id)
.retrieve().bodyToMono(Ingredient.class);
}
When I access to localhost:8082/ingredients/FLTO a node of my webapp, it shows me only the relative path like this
{
"id": "FLTO",
"name": "Flour Tortilla",
"type": "WRAP",
"_links": {
"self": {
"href": "/ingredients/FLTO"
}
}
}
I've tried the WebMvcLinkBuilder but it still did not work correctly. I found some explanations about my problem. But I'm not sure whether the context/exchange was null (and why). Could you help me?
try to set spring.main.web-application-type=reactive
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"
}
]
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;
}
I am working with vertx and java, I am new to vertx and i have an api. please go to link below for a better understanding
https://www.tez-tour.com/tariffsearch/hotels?countryId=1104&cityId=345&locale=en (countryId (destination)= Turkey, cityId (city of depature)= Moscow)
and a python example script to fetch data from tour api
import asyncio
from aiohttp import ClientSession
import json
async def do_request(url):
async with ClientSession() as session:
async with session.get(url) as response:
resp = await response.read()
#print(str(resp, 'utf-8'))
parsed = json.loads(str(resp, 'utf-8'))
print(parsed)
tasks = []
tourCities = {
/*'Turkey': {
'cities': [1285,12689,12706,143330,9004247,4433,5736,139343,4434,12691,21301,12705,149827,4151426]
},*/
'Austria': {
'tourId': [308122,3024267,147579,353869,320460,3024283,253138,3026464,3024262,293808,469713,3024272,314293,467029,348518,544505,384331,594027,3025654,258494],
'params': {
'hotelClasses': [ // Типы отелей
{"classId": 269506,"name": "Special Cat.","weight": -9},
{"classId": 261942,"name": "Chalet","weight": -8},
{"classId": 253005,"name": "Apts","weight": -6},
{"classId": 253006,"name": "Pens","weight": -5},
],
"tourTypes": [ // Состав тура
{"id": 1,"name": "Полный пакет","haveResidence": true,"haveTransfer": true,"haveFly": true,"haveInsurance": true},
{"id": 2,"name": "Проживание+трансфер","haveResidence": true,"haveTransfer": true,"haveFly": false,"haveInsurance": true},
],
"pansion": [ // Пансион
{"rAndBId": 15350,"name": "Размещение без питания","weight": 0,"sortOrder": 0},
{"rAndBId": 2424,"name": "Только завтраки","weight": 1,"sortOrder": 1},
{"rAndBId": 2474,"name": "Завтрак и ужин","weight": 3,"sortOrder": 3},
],
"tours": [ // Регионы (города, куда ищем тур)
{"name": "Бад-Кляйнкирхайм","tourId": [308122]},
{"name": "Баден","tourId": [3024267]},
tour operator has a web site (or external REST API) where from we can fetch tour data
each of them provide us with authentication data (login & password) to connect to their tour database (no jdbc, only web based access)
So i have some interface to be implemented and i should use WebClient but i dont fully understand how to write this method to fetch from the api above
I have two method to implement as follows
#Override
public YuService runParserTask(String tourOperator, Handler<AsyncResult<Void>> handler) {
return this;
}
#Override
public YuService getTaskStatus(String tourOperator, Handler<AsyncResult<ParseTask>> handler) {
return this;
}
and a parser dto with ENUM status as follows
#DataObject(generateConverter = true)
public class ParseTask {
private String type;
private Status status;
public ParseTask(String type, Status status) {
this.type = type;
this.status = status;
}
public ParseTask(JsonObject json) {
ParseTaskConverter.fromJson(json, this);
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
ParseTaskConverter.toJson(this, json);
return json;
}
Can i get an explanation may be a bit of code to help me get a better understanding on how to implement this method
In the new version of Spring Data (Fowler), one can pass an interface to Spring MVC controller actions, and Spring Data will then automatically create a proxy implementation and bind values to this proxy object.
An example is given in the blog post that describes some of the new features in Spring Data Fowler:
interface Form {
#NotBlank String getName();
#NotBlank String getText();
}
#Controller
#RequestMapping(value = "/guestbook")
class GuestbookController {
#RequestMapping(method = RequestMethod.GET)
String guestbook(Form form, Model model) { ... }
#RequestMapping(method = RequestMethod.POST)
String guestbook(#Valid Form form, Errors errors, Model model) { ... }
}
My question is if this can also be done when deserializing JSON with Jackson? For instance, like this:
interface Form {
#NotBlank String getName();
#NotBlank String getText();
}
#Controller
#RequestMapping(value = "/guestbook")
class GuestbookController {
#RequestMapping(method = RequestMethod.POST)
String guestbook(#Valid #RequestBody Form form) { ... }
}
However, this gives the following exception:
Can not construct instance of Form, problem: abstract types either
need to be mapped to concrete types, have custom deserializer, or be
instantiated with additional type information
I understand what the problem is, but is there a solution that does not require me to create a class that implements my interface or write a lot of code? One that is simpler than this approach. Because otherwise I might as well go with a DTO approach, but I just found it to be super cool if I could simply use an interface as in the example.
I can use a DTO class just fine with the above approach (or avoid using JSON), but using an interface like in the blog post's example would be neat. But is this possible with the Jackson library when deserializing JSON?
You could use Jackson Mr Bean module, which does exactly what you want.
To use it, just download the specific dependency and set it as a module to the underlying ObjectMapper instance:
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.mrbean.MrBeanModule;
public class AbstractPojosExample {
public interface Person {
String getName();
int getAge();
Address getAddress();
default String asString() {
return String.format("%s, %d, %s", this.getName(), this.getAge(), this.getAddress().asString());
}
}
public interface Address {
String getStreet();
String getCity();
default String asString() {
return String.format("%s, %s", this.getStreet(), this.getCity());
}
}
private void run() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());
String json = "[ { \"name\": \"John\", \"age\": 23, "
+ "\"address\": { \"street\": \"1001 5th Ave\", \"city\": \"New York\" } }, "
+ "{ \"name\": \"Jean Jacques\", \"age\": 26 , "
+ "\"address\": { \"street\": \"1001 Champs Elysees Blvd\", \"city\": \"Paris\" } }, "
+ "{ \"name\": \"Oscar Ruben\", \"age\": 54, "
+ "\"address\": { \"street\": \"1001 9th July Ave\", \"city\": \"Buenos Aires\" } } ]";
System.out.println(json);
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
people.stream().map(Person::asString).forEach(System.out::println);
}
public static void main(String[] args) throws IOException {
AbstractPojosExample example = new AbstractPojosExample();
example.run();
}
}
For the given json:
[
{
"name": "John",
"age": 23,
"address": {
"street": "1001 5th Ave",
"city": "New York"
}
},
{
"name": "Jean Jacques",
"age": 26,
"address": {
"street": "1001 Champs Elysees Blvd",
"city": "Paris"
}
},
{
"name": "Oscar Ruben",
"age": 54,
"address": {
"street": "1001 9th July Ave",
"city": "Buenos Aires"
}
}
]
The little program above produces the following output:
John, 23, 1001 5th Ave, New York
Jean Jacques, 26, 1001 Champs Elysees Blvd, Paris
Oscar Ruben, 54, 1001 9th July Ave, Buenos Aires