Return Map from GraphQL (#11966)
* Return Map from GraphQL Three techniques have been used 1. Return as Json String 2. Return Json using GraphQL scalar type 3. Return as list of key-value pair * Adding custom scalar in plugin configuration * Build failure fix 1. Integrating the .graphqls files 2. Updating the respective query resolvers * Build failure Fix 1. Removed the extra .graphql file 2. Added ExtendedGraphQLScalarType class because the parent class didn't have default constructor and the client code generation plugin was requiring it. * Code refactoring * Code refactoring
This commit is contained in:
parent
936055fd69
commit
ce2839b058
|
@ -36,6 +36,21 @@
|
|||
<artifactId>graphql-java-annotations</artifactId>
|
||||
<version>${graphql-java-annotations.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.ratpack</groupId>
|
||||
<artifactId>ratpack-core</artifactId>
|
||||
<version>${ratpack-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.americanexpress.nodes</groupId>
|
||||
<artifactId>nodes</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java</artifactId>
|
||||
<version>11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java-tools</artifactId>
|
||||
|
@ -95,6 +110,14 @@
|
|||
<version>${mockserver-client-java.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java-extended-scalars</artifactId>
|
||||
<version>${graphql-java-extended-scalars.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -133,6 +156,13 @@
|
|||
<copyRuntimeSources>false</copyRuntimeSources>
|
||||
<generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
|
||||
<separateUtilityClasses>true</separateUtilityClasses>
|
||||
<customScalars>
|
||||
<customScalar>
|
||||
<graphQLTypeName>JSON</graphQLTypeName>
|
||||
<javaType>java.util.Map</javaType>
|
||||
<graphQLScalarTypeClass>com.baeldung.graphqlreturnmap.ExtendedGraphQLScalarType</graphQLScalarTypeClass>
|
||||
</customScalar>
|
||||
</customScalars>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@ -154,6 +184,8 @@
|
|||
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<graphql.java.generator.version>1.18</graphql.java.generator.version>
|
||||
<graphql-java-extended-scalars.version>2022-04-06T00-10-27-a70541e</graphql-java-extended-scalars.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.baeldung.graphql.server;
|
|||
|
||||
import com.baeldung.graphql.data.Book;
|
||||
import com.baeldung.graphql.data.BookRepository;
|
||||
import com.baeldung.graphqlreturnmap.entity.Product;
|
||||
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -18,4 +19,12 @@ public class GraphQLQuery implements GraphQLQueryResolver {
|
|||
return repository.getAllBooks();
|
||||
}
|
||||
|
||||
public List<Product> getProducts(int pageSize, int pageNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Product getProduct(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package com.baeldung.graphqlreturnmap;
|
||||
|
||||
import com.baeldung.graphql.utils.SchemaUtils;
|
||||
import com.baeldung.graphqlreturnmap.resolver.ProductResolver;
|
||||
import com.baeldung.graphqlreturnmap.resolver.Query;
|
||||
import com.coxautodev.graphql.tools.SchemaParser;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import graphql.scalars.ExtendedScalars;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import ratpack.handling.Context;
|
||||
import ratpack.handling.Handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static ratpack.jackson.Jackson.json;
|
||||
|
||||
public class AppHandler implements Handler {
|
||||
private static final Logger LOGGER = Logger.getLogger(AppHandler.class.getSimpleName());
|
||||
private GraphQL graphql;
|
||||
|
||||
public AppHandler() throws Exception {
|
||||
GraphQLSchema schema = SchemaParser.newParser()
|
||||
.resolvers(new Query(), new ProductResolver())
|
||||
.scalars(ExtendedScalars.Json)
|
||||
.file("schema.graphqls")
|
||||
.build()
|
||||
.makeExecutableSchema();
|
||||
graphql = GraphQL.newGraphQL(schema).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Context context) throws Exception {
|
||||
context.parse(Map.class)
|
||||
.then(payload -> {
|
||||
ExecutionResult executionResult = graphql.execute(payload.get(SchemaUtils.QUERY)
|
||||
.toString(), null, this, Collections.emptyMap());
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
if (executionResult.getErrors()
|
||||
.isEmpty()) {
|
||||
result.put(SchemaUtils.DATA, executionResult.getData());
|
||||
} else {
|
||||
result.put(SchemaUtils.ERRORS, executionResult.getErrors());
|
||||
LOGGER.warning("Errors: " + executionResult.getErrors());
|
||||
}
|
||||
context.render(json(result));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.baeldung.graphqlreturnmap;
|
||||
|
||||
import graphql.language.ScalarTypeDefinition;
|
||||
import graphql.schema.Coercing;
|
||||
import graphql.schema.GraphQLDirective;
|
||||
import graphql.schema.GraphQLScalarType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ExtendedGraphQLScalarType extends GraphQLScalarType {
|
||||
|
||||
public ExtendedGraphQLScalarType(){
|
||||
super("","",null);
|
||||
|
||||
}
|
||||
|
||||
public ExtendedGraphQLScalarType(String name, String description, Coercing coercing) {
|
||||
super(name, description, coercing);
|
||||
}
|
||||
|
||||
public ExtendedGraphQLScalarType(String name, String description, Coercing coercing, List<GraphQLDirective> directives, ScalarTypeDefinition definition) {
|
||||
super(name, description, coercing, directives, definition);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.graphqlreturnmap;
|
||||
|
||||
import ratpack.server.RatpackServer;
|
||||
|
||||
public class GraphqlReturnMap {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final RatpackServer server = RatpackServer.of(s -> s.handlers(chain -> chain.post("product", new AppHandler())));
|
||||
server.start();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.baeldung.graphqlreturnmap.entity;
|
||||
|
||||
public class Attribute {
|
||||
private String name;
|
||||
private String description;
|
||||
private String unit;
|
||||
|
||||
public Attribute(String name, String description, String unit){
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Attribute{" +
|
||||
"name='" + name + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", unit='" + unit + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.baeldung.graphqlreturnmap.entity;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Product {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String description;
|
||||
private Map<String, Attribute> attributes;
|
||||
|
||||
|
||||
public Product(){
|
||||
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Map<String, Attribute> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, Attribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.baeldung.graphqlreturnmap.model;
|
||||
|
||||
|
||||
import com.baeldung.graphqlreturnmap.entity.Attribute;
|
||||
|
||||
public class AttributeKeyValueModel {
|
||||
private String key;
|
||||
private Attribute value;
|
||||
|
||||
public AttributeKeyValueModel(String key, Attribute value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Attribute getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Attribute value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.graphqlreturnmap.repository;
|
||||
|
||||
|
||||
import com.baeldung.graphqlreturnmap.entity.Product;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ProductRepository {
|
||||
List<Product> getProducts(Integer pageSize, Integer pageNumber);
|
||||
Product getProduct(Integer id);
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.baeldung.graphqlreturnmap.repository.impl;
|
||||
|
||||
import com.baeldung.graphqlreturnmap.entity.Attribute;
|
||||
import com.baeldung.graphqlreturnmap.entity.Product;
|
||||
import com.baeldung.graphqlreturnmap.repository.ProductRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Repository
|
||||
public class ProductRepositoryImpl implements ProductRepository {
|
||||
|
||||
private static List<Product> productList = new ArrayList<>();
|
||||
|
||||
public ProductRepositoryImpl() {
|
||||
for (int i = 1; i <= 10; i++){
|
||||
Product product = new Product();
|
||||
product.setId(i);
|
||||
product.setName(String.format("Product %d", i));
|
||||
product.setDescription(String.format("Product %d description", i));
|
||||
product.setAttributes(createAttributes(i));
|
||||
productList.add(product);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Attribute> createAttributes(int i) {
|
||||
Map<String, Attribute> attributeMap = new HashMap<>();
|
||||
attributeMap.put(String.format("attribute_%d",i), new Attribute(String.format("Attribute%d name",i),"This is custom attribute description","This is custom attribute unit"));
|
||||
attributeMap.put("size", new Attribute((i & 1) == 0 ? "Small" : "Large","This is custom attribute description","This is custom attribute unit"));
|
||||
return attributeMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Product> getProducts(Integer pageSize, Integer pageNumber) {
|
||||
return productList.stream().skip(pageSize*pageNumber).limit(pageSize).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Product getProduct(Integer id) {
|
||||
return productList.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.baeldung.graphqlreturnmap.resolver;
|
||||
|
||||
import com.baeldung.graphqlreturnmap.entity.Product;
|
||||
import com.baeldung.graphqlreturnmap.model.AttributeKeyValueModel;
|
||||
import com.coxautodev.graphql.tools.GraphQLResolver;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ProductResolver implements GraphQLResolver<Product> {
|
||||
public ProductResolver(){
|
||||
}
|
||||
|
||||
public List<AttributeKeyValueModel> getAttribute_list(Product product){
|
||||
List<AttributeKeyValueModel> attributeModelList = new LinkedList<>();
|
||||
product.getAttributes().forEach((key, val) -> attributeModelList.add(new AttributeKeyValueModel(key, val)));
|
||||
return attributeModelList;
|
||||
}
|
||||
|
||||
public String getAttribute_string(Product product){
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(product.getAttributes());
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.baeldung.graphqlreturnmap.resolver;
|
||||
|
||||
import com.baeldung.graphql.data.Book;
|
||||
import com.baeldung.graphqlreturnmap.entity.Product;
|
||||
import com.baeldung.graphqlreturnmap.repository.ProductRepository;
|
||||
import com.baeldung.graphqlreturnmap.repository.impl.ProductRepositoryImpl;
|
||||
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Query implements GraphQLQueryResolver {
|
||||
|
||||
@Autowired
|
||||
private ProductRepository productRepository;
|
||||
public Query(){
|
||||
productRepository = new ProductRepositoryImpl();
|
||||
}
|
||||
|
||||
public List<Product> getProducts(int pageSize, int pageNumber) {
|
||||
return productRepository.getProducts(pageSize, pageNumber);
|
||||
}
|
||||
|
||||
public Product getProduct(int id) {
|
||||
return productRepository.getProduct(id);
|
||||
}
|
||||
|
||||
public List<Book> allBooks() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -10,8 +10,34 @@ type Author {
|
|||
|
||||
type Query {
|
||||
allBooks: [Book]
|
||||
products(size: Int, page: Int): [Product]!
|
||||
product(id: Int): Product!
|
||||
}
|
||||
|
||||
|
||||
|
||||
type Product {
|
||||
id: ID
|
||||
name: String!
|
||||
description: String
|
||||
attribute_string:String
|
||||
attribute_list:[AttributeKeyValuePair]
|
||||
attributes: JSON
|
||||
}
|
||||
|
||||
type AttributeKeyValuePair {
|
||||
key:String
|
||||
value:Attribute
|
||||
}
|
||||
|
||||
type Attribute {
|
||||
name:String
|
||||
description:String
|
||||
unit:String
|
||||
}
|
||||
scalar JSON
|
||||
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
}
|
Loading…
Reference in New Issue