diff --git a/spring-boot-modules/spring-boot-libraries-2/pom.xml b/spring-boot-modules/spring-boot-libraries-2/pom.xml index 04c09754b4..f62a341efc 100644 --- a/spring-boot-modules/spring-boot-libraries-2/pom.xml +++ b/spring-boot-modules/spring-boot-libraries-2/pom.xml @@ -57,6 +57,11 @@ ${awaitility.version} test + + io.leangen.graphql + spqr + 0.11.2 + diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/Book.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/Book.java new file mode 100644 index 0000000000..c6ff9e515a --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/Book.java @@ -0,0 +1,57 @@ +package com.baeldung.sprq; + +import java.util.Objects; + +public class Book { + private Integer id; + private String author; + private String title; + + public Book(Integer id, String author, String title) { + this.id = id; + this.author = author; + this.title = title; + } + + public Book() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Book book = (Book) o; + return id.equals(book.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookResolver.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookResolver.java new file mode 100644 index 0000000000..747d52f0af --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookResolver.java @@ -0,0 +1,42 @@ +package com.baeldung.sprq; + +import io.leangen.graphql.annotations.GraphQLArgument; +import io.leangen.graphql.annotations.GraphQLMutation; +import io.leangen.graphql.annotations.GraphQLQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class BookResolver { + + @Autowired + IBookService bookService; + + @GraphQLQuery(name = "getBookWithTitle") + public Book getBookWithTitle(@GraphQLArgument(name = "title") String title) { + return bookService.getBookWithTitle(title); + } + + @GraphQLQuery(name = "getAllBooks", description = "Get all books") + public List getAllBooks() { + return bookService.getAllBooks(); + } + + @GraphQLMutation(name = "addBook") + public Book addBook(@GraphQLArgument(name = "newBook") Book book) { + return bookService.addBook(book); + } + + @GraphQLMutation(name = "updateBook") + public Book updateBook(@GraphQLArgument(name = "modifiedBook") Book book) { + return bookService.updateBook(book); + } + + @GraphQLMutation(name = "deleteBook") + public void deleteBook(@GraphQLArgument(name = "book") Book book) { + bookService.deleteBook(book); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookService.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookService.java new file mode 100644 index 0000000000..6dbfe9c6f9 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookService.java @@ -0,0 +1,48 @@ +package com.baeldung.sprq; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +public class BookService implements IBookService { + + Set books = new HashSet<>(); + + @Override + public Book getBookWithTitle(String title) { + return books.stream() + .filter(book -> book.getTitle() + .equals(title)) + .findFirst() + .orElse(null); + } + + @Override + public List getAllBooks() { + return books.stream() + .collect(Collectors.toList()); + } + + @Override + public Book addBook(Book book) { + books.add(book); + return book; + } + + @Override + public Book updateBook(Book book) { + books.remove(book); + books.add(book); + return book; + } + + @Override + public boolean deleteBook(Book book) { + return books.remove(book); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/GraphqlController.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/GraphqlController.java new file mode 100644 index 0000000000..b62bdbd6a8 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/GraphqlController.java @@ -0,0 +1,35 @@ +package com.baeldung.sprq; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.GraphQLException; +import graphql.schema.GraphQLSchema; +import io.leangen.graphql.GraphQLSchemaGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +@RestController +public class GraphqlController { + + private final GraphQL graphQL; + + @Autowired + public GraphqlController(BookResolver bookResolver) { + GraphQLSchema schema = new GraphQLSchemaGenerator().withBasePackages("com.baeldung") + .withOperationsFromSingleton(bookResolver) + .generate(); + this.graphQL = new GraphQL.Builder(schema).build(); + } + + @PostMapping(value = "/graphql") + public Map execute(@RequestBody Map request, HttpServletRequest raw) throws GraphQLException { + ExecutionResult result = graphQL.execute(request.get("query")); + return result.getData(); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/IBookService.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/IBookService.java new file mode 100644 index 0000000000..1c1257c178 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/IBookService.java @@ -0,0 +1,15 @@ +package com.baeldung.sprq; + +import java.util.List; + +public interface IBookService { + Book getBookWithTitle(String title); + + List getAllBooks(); + + Book addBook(Book book); + + Book updateBook(Book book); + + boolean deleteBook(Book book); +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-2/src/test/java/com/baeldung/sprq/GraphqlControllerIntegrationTest.java b/spring-boot-modules/spring-boot-libraries-2/src/test/java/com/baeldung/sprq/GraphqlControllerIntegrationTest.java new file mode 100644 index 0000000000..65b6ff1e0b --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-2/src/test/java/com/baeldung/sprq/GraphqlControllerIntegrationTest.java @@ -0,0 +1,60 @@ +package com.baeldung.sprq; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +public class GraphqlControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + BookService bookService; + + private static final String GRAPHQL_PATH = "/graphql"; + + @Test + public void givenNoBooks_whenReadAll_thenStatusIsOk() throws Exception { + + String getAllBooksQuery = "{\n" + " getAllBooks {\n" + " id\n" + " author\n" + " title\n" + " }\n" + "}\n"; + + this.mockMvc.perform(post(GRAPHQL_PATH).content(toJSON(getAllBooksQuery)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.getAllBooks").isEmpty()); + } + + @Test + public void whenAddBook_thenStatusIsOk() throws Exception { + + String addBookMutation = "mutation {\n" + " addBook(newBook: {id: 123, author: \"J.R.R. Tolkien\", title: \"The Lord of the Rings\"}) {\n" + " id\n" + " author\n" + " title\n" + " }\n" + "}\n"; + + this.mockMvc.perform(post(GRAPHQL_PATH).content(toJSON(addBookMutation)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.addBook.id").value("123")) + .andExpect(jsonPath("$.addBook.author").value("J.R.R. Tolkien")) + .andExpect(jsonPath("$.addBook.title").value("The Lord of the Rings")); + } + + private String toJSON(String query) throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("query", query); + return jsonObject.toString(); + } +} \ No newline at end of file