diff --git a/graphql/graphql-java/pom.xml b/graphql/graphql-java/pom.xml index 4e2444c1fb..f2733bf671 100644 --- a/graphql/graphql-java/pom.xml +++ b/graphql/graphql-java/pom.xml @@ -12,8 +12,24 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT + ../../ + + + jitpack.io + https://jitpack.io + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + com.graphql-java @@ -25,11 +41,114 @@ ratpack-core ${ratpack-core.version} + + com.github.americanexpress.nodes + nodes + 0.5.0 + + + com.graphql-java + graphql-java + 9.2 + + + com.graphql-java + graphql-java-tools + 5.2.4 + + + com.graphql-java + graphql-java-servlet + 6.1.3 + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.mock-server + mockserver-netty + 5.11.2 + + + org.mock-server + mockserver-client-java + 5.11.2 + + + com.graphql-java-generator + graphql-java-runtime + ${graphql.java.generator.version} + + + org.junit.jupiter + junit-jupiter-engine + 5.8.2 + test + + + org.assertj + assertj-core + 3.22.0 + test + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.eclipse.jetty + jetty-maven-plugin + 10.0.7 + + + maven-war-plugin + 3.1.0 + + + com.graphql-java-generator + graphql-maven-plugin + ${graphql.java.generator.version} + + + + generateClientCode + + + + + com.baeldung.graphql.generated + false + false + true + + + + + 3.0.3 1.4.6 + 3.1 + 1.8 + 1.8 + 1.18 \ No newline at end of file diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/clients/AmericanExpressNodes.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/clients/AmericanExpressNodes.java new file mode 100644 index 0000000000..446dbd416e --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/clients/AmericanExpressNodes.java @@ -0,0 +1,24 @@ +package com.baeldung.graphql.clients; + +import com.baeldung.graphql.data.Data; +import io.aexp.nodes.graphql.GraphQLRequestEntity; +import io.aexp.nodes.graphql.GraphQLResponseEntity; +import io.aexp.nodes.graphql.GraphQLTemplate; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; + +public class AmericanExpressNodes { + + public static GraphQLResponseEntity callGraphQLService(String url, String query) throws IOException { + GraphQLTemplate graphQLTemplate = new GraphQLTemplate(); + + GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder() + .url(StringUtils.join(url, "?query=", query)) + .request(Data.class) + .build(); + + return graphQLTemplate.query(requestEntity, Data.class); + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/clients/ApacheHttpClient.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/clients/ApacheHttpClient.java new file mode 100644 index 0000000000..d4f88e62f5 --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/clients/ApacheHttpClient.java @@ -0,0 +1,25 @@ +package com.baeldung.graphql.clients; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.HttpClientBuilder; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +public class ApacheHttpClient { + + public static HttpResponse callGraphQLService(String url, String query) throws URISyntaxException, IOException { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(url); + URI uri = new URIBuilder(request.getURI()) + .addParameter("query", query) + .build(); + request.setURI(uri); + return client.execute(request); + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Author.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Author.java new file mode 100644 index 0000000000..876df27769 --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Author.java @@ -0,0 +1,31 @@ +package com.baeldung.graphql.data; + +import org.apache.commons.lang3.StringUtils; + +public class Author { + + private String name; + private String surname; + + public Author() { + + } + + public Author(String name, String surname) { + this.name = name; + this.surname = surname; + } + + public String getName() { + return name; + } + + public String getSurname() { + return surname; + } + + public String getFullName() { + return StringUtils.join(getName(), " ", getSurname()); + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Book.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Book.java new file mode 100644 index 0000000000..6e6188368b --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Book.java @@ -0,0 +1,25 @@ +package com.baeldung.graphql.data; + +public class Book { + + private String title; + private Author author; + + public Book() { + + } + + public Book(String title, Author author) { + this.title = title; + this.author = author; + } + + public String getTitle() { + return title; + } + + public Author getAuthor() { + return author; + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/BookRepository.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/BookRepository.java new file mode 100644 index 0000000000..f812ed088d --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/BookRepository.java @@ -0,0 +1,19 @@ +package com.baeldung.graphql.data; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class BookRepository { + + private static final List books = Stream.of( + new Book("Title 1", new Author("Pero", "Peric")), + new Book("Title 2", new Author("Marko", "Maric")) + ).collect(Collectors.toList()); + + public List getAllBooks() { + return Collections.unmodifiableList(books); + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Data.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Data.java new file mode 100644 index 0000000000..63263a496b --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Data.java @@ -0,0 +1,22 @@ +package com.baeldung.graphql.data; + +import java.util.Collections; +import java.util.List; + +public class Data { + + private List allBooks; + + public Data() { + + } + + public Data(List allBooks) { + this.allBooks = allBooks; + } + + public List getAllBooks() { + return Collections.unmodifiableList(allBooks); + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Response.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Response.java new file mode 100644 index 0000000000..eedd13e04e --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/data/Response.java @@ -0,0 +1,19 @@ +package com.baeldung.graphql.data; + +public class Response { + + private Data data; + + public Response() { + + } + + public Response(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/server/GraphQLEndpoint.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/server/GraphQLEndpoint.java new file mode 100644 index 0000000000..c69144c9f5 --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/server/GraphQLEndpoint.java @@ -0,0 +1,36 @@ +package com.baeldung.graphql.server; + +import com.baeldung.graphql.data.BookRepository; +import com.coxautodev.graphql.tools.SchemaParser; +import graphql.schema.GraphQLSchema; +import graphql.servlet.SimpleGraphQLHttpServlet; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebServlet(urlPatterns = "/graphql") +public class GraphQLEndpoint extends HttpServlet { + + private SimpleGraphQLHttpServlet graphQLServlet; + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + graphQLServlet.service(req, resp); + } + + @Override + public void init() { + GraphQLSchema schema = SchemaParser.newParser() + .resolvers(new GraphQLQuery(new BookRepository())) + .file("schema.graphqls") + .build() + .makeExecutableSchema(); + graphQLServlet = SimpleGraphQLHttpServlet + .newBuilder(schema) + .build(); + } +} diff --git a/graphql/graphql-java/src/main/java/com/baeldung/graphql/server/GraphQLQuery.java b/graphql/graphql-java/src/main/java/com/baeldung/graphql/server/GraphQLQuery.java new file mode 100644 index 0000000000..8ba9fa25c5 --- /dev/null +++ b/graphql/graphql-java/src/main/java/com/baeldung/graphql/server/GraphQLQuery.java @@ -0,0 +1,21 @@ +package com.baeldung.graphql.server; + +import com.baeldung.graphql.data.Book; +import com.baeldung.graphql.data.BookRepository; +import com.coxautodev.graphql.tools.GraphQLQueryResolver; + +import java.util.List; + +public class GraphQLQuery implements GraphQLQueryResolver { + + private final BookRepository repository; + + public GraphQLQuery(BookRepository repository) { + this.repository = repository; + } + + public List allBooks() { + return repository.getAllBooks(); + } + +} diff --git a/graphql/graphql-java/src/main/resources/schema.graphqls b/graphql/graphql-java/src/main/resources/schema.graphqls new file mode 100644 index 0000000000..b0834e04b7 --- /dev/null +++ b/graphql/graphql-java/src/main/resources/schema.graphqls @@ -0,0 +1,17 @@ +type Book { + title: String! + author: Author +} + +type Author { + name: String! + surname: String! +} + +type Query { + allBooks: [Book] +} + +schema { + query: Query +} \ No newline at end of file diff --git a/graphql/graphql-java/src/test/java/com/baeldung/graphql/GraphQLMockServer.java b/graphql/graphql-java/src/test/java/com/baeldung/graphql/GraphQLMockServer.java new file mode 100644 index 0000000000..fb5a789428 --- /dev/null +++ b/graphql/graphql-java/src/test/java/com/baeldung/graphql/GraphQLMockServer.java @@ -0,0 +1,85 @@ +package com.baeldung.graphql; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.mockserver.client.MockServerClient; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpStatusCode; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URISyntaxException; + +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.matchers.Times.exactly; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +public class GraphQLMockServer { + + public static ClientAndServer mockServer; + public static String serviceUrl; + + private static int serverPort; + + public static final String SERVER_ADDRESS = "127.0.0.1"; + public static final String HTTP_GET_POST = "GET"; + public static final String PATH = "/graphql"; + + @BeforeAll + static void startServer() throws IOException, URISyntaxException { + serverPort = getFreePort(); + serviceUrl = "http://" + SERVER_ADDRESS + ":" + serverPort + PATH; + mockServer = startClientAndServer(serverPort); + mockAllBooksTitleRequest(); + mockAllBooksTitleAuthorRequest(); + } + + @AfterAll + static void stopServer() { + mockServer.stop(); + } + + private static void mockAllBooksTitleAuthorRequest() { + String requestQuery = "{allBooks{title,author{name,surname}}}"; + String responseJson = "{\"data\":{\"allBooks\":[{\"title\":\"Title 1\",\"author\":{\"name\":\"Pero\",\"surname\":\"Peric\"}},{\"title\":\"Title 2\",\"author\":{\"name\":\"Marko\",\"surname\":\"Maric\"}}]}}"; + + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH) + .withQueryStringParameter("query", requestQuery), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatusCode.OK_200.code()) + .withBody(responseJson) + ); + } + + private static void mockAllBooksTitleRequest() { + String requestQuery = "{allBooks{title}}"; + String responseJson = "{\"data\":{\"allBooks\":[{\"title\":\"Title 1\"},{\"title\":\"Title 2\"}]}}"; + + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH) + .withQueryStringParameter("query", requestQuery), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatusCode.OK_200.code()) + .withBody(responseJson) + ); + } + + private static int getFreePort () throws IOException { + try (ServerSocket serverSocket = new ServerSocket(0)) { + return serverSocket.getLocalPort(); + } + } + +} diff --git a/graphql/graphql-java/src/test/java/com/baeldung/graphql/clients/AmericanExpressNodesUnitTest.java b/graphql/graphql-java/src/test/java/com/baeldung/graphql/clients/AmericanExpressNodesUnitTest.java new file mode 100644 index 0000000000..8466cdee76 --- /dev/null +++ b/graphql/graphql-java/src/test/java/com/baeldung/graphql/clients/AmericanExpressNodesUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.graphql.clients; + +import com.baeldung.graphql.GraphQLMockServer; +import com.baeldung.graphql.data.Data; +import io.aexp.nodes.graphql.GraphQLResponseEntity; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +class AmericanExpressNodesUnitTest extends GraphQLMockServer { + + @Test + void givenGraphQLEndpoint_whenRequestingAllBooksWithTitle_thenExpectedJsonIsReturned() throws IOException { + GraphQLResponseEntity responseEntity = AmericanExpressNodes.callGraphQLService(serviceUrl, "{allBooks{title}}"); + + assertAll( + () -> assertThat(responseEntity.getResponse().getAllBooks()).hasSize(2), + () -> assertThat(responseEntity.getResponse().getAllBooks().get(0).getTitle()).isEqualTo("Title 1"), + () -> assertThat(responseEntity.getResponse().getAllBooks().get(1).getTitle()).isEqualTo("Title 2") + ); + } + + @Test + void givenGraphQLEndpoint_whenRequestingAllBooksWithTitleAndAuthor_thenExpectedJsonIsReturned() throws IOException { + GraphQLResponseEntity responseEntity = AmericanExpressNodes.callGraphQLService(serviceUrl, "{allBooks{title,author{name,surname}}}"); + + assertAll( + () -> assertThat(responseEntity.getResponse().getAllBooks()).hasSize(2), + () -> assertThat(responseEntity.getResponse().getAllBooks().get(0).getTitle()).isEqualTo("Title 1"), + () -> assertThat(responseEntity.getResponse().getAllBooks().get(0).getAuthor().getFullName()).isEqualTo("Pero Peric"), + () -> assertThat(responseEntity.getResponse().getAllBooks().get(1).getTitle()).isEqualTo("Title 2"), + () -> assertThat(responseEntity.getResponse().getAllBooks().get(1).getAuthor().getFullName()).isEqualTo("Marko Maric") + ); + } + +} diff --git a/graphql/graphql-java/src/test/java/com/baeldung/graphql/clients/ApacheHttpClientUnitTest.java b/graphql/graphql-java/src/test/java/com/baeldung/graphql/clients/ApacheHttpClientUnitTest.java new file mode 100644 index 0000000000..17d97e0d14 --- /dev/null +++ b/graphql/graphql-java/src/test/java/com/baeldung/graphql/clients/ApacheHttpClientUnitTest.java @@ -0,0 +1,49 @@ +package com.baeldung.graphql.clients; + +import com.baeldung.graphql.GraphQLMockServer; +import com.baeldung.graphql.data.Response; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +class ApacheHttpClientUnitTest extends GraphQLMockServer { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void givenGraphQLEndpoint_whenRequestingAllBooksWithTitle_thenExpectedJsonIsReturned() throws IOException, URISyntaxException { + HttpResponse httpResponse = ApacheHttpClient.callGraphQLService(serviceUrl, "{allBooks{title}}"); + String actualResponse = IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8.name()); + Response parsedResponse = objectMapper.readValue(actualResponse, Response.class); + + assertAll( + () -> assertThat(parsedResponse.getData().getAllBooks()).hasSize(2), + () -> assertThat(parsedResponse.getData().getAllBooks().get(0).getTitle()).isEqualTo("Title 1"), + () -> assertThat(parsedResponse.getData().getAllBooks().get(1).getTitle()).isEqualTo("Title 2") + ); + } + + @Test + void givenGraphQLEndpoint_whenRequestingAllBooksWithTitleAndAuthor_thenExpectedJsonIsReturned() throws IOException, URISyntaxException { + HttpResponse httpResponse = ApacheHttpClient.callGraphQLService(serviceUrl, "{allBooks{title,author{name,surname}}}"); + String actualResponse = IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8.name()); + Response parsedResponse = objectMapper.readValue(actualResponse, Response.class); + + assertAll( + () -> assertThat(parsedResponse.getData().getAllBooks()).hasSize(2), + () -> assertThat(parsedResponse.getData().getAllBooks().get(0).getTitle()).isEqualTo("Title 1"), + () -> assertThat(parsedResponse.getData().getAllBooks().get(0).getAuthor().getFullName()).isEqualTo("Pero Peric"), + () -> assertThat(parsedResponse.getData().getAllBooks().get(1).getTitle()).isEqualTo("Title 2"), + () -> assertThat(parsedResponse.getData().getAllBooks().get(1).getAuthor().getFullName()).isEqualTo("Marko Maric") + ); + } + +}