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>
|
<artifactId>graphql-java-annotations</artifactId>
|
||||||
<version>${graphql-java-annotations.version}</version>
|
<version>${graphql-java-annotations.version}</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>com.graphql-java</groupId>
|
<groupId>com.graphql-java</groupId>
|
||||||
<artifactId>graphql-java-tools</artifactId>
|
<artifactId>graphql-java-tools</artifactId>
|
||||||
|
@ -95,6 +110,14 @@
|
||||||
<version>${mockserver-client-java.version}</version>
|
<version>${mockserver-client-java.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.graphql-java</groupId>
|
||||||
|
<artifactId>graphql-java-extended-scalars</artifactId>
|
||||||
|
<version>${graphql-java-extended-scalars.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -133,6 +156,13 @@
|
||||||
<copyRuntimeSources>false</copyRuntimeSources>
|
<copyRuntimeSources>false</copyRuntimeSources>
|
||||||
<generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
|
<generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
|
||||||
<separateUtilityClasses>true</separateUtilityClasses>
|
<separateUtilityClasses>true</separateUtilityClasses>
|
||||||
|
<customScalars>
|
||||||
|
<customScalar>
|
||||||
|
<graphQLTypeName>JSON</graphQLTypeName>
|
||||||
|
<javaType>java.util.Map</javaType>
|
||||||
|
<graphQLScalarTypeClass>com.baeldung.graphqlreturnmap.ExtendedGraphQLScalarType</graphQLScalarTypeClass>
|
||||||
|
</customScalar>
|
||||||
|
</customScalars>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
@ -154,6 +184,8 @@
|
||||||
|
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<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>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -2,6 +2,7 @@ package com.baeldung.graphql.server;
|
||||||
|
|
||||||
import com.baeldung.graphql.data.Book;
|
import com.baeldung.graphql.data.Book;
|
||||||
import com.baeldung.graphql.data.BookRepository;
|
import com.baeldung.graphql.data.BookRepository;
|
||||||
|
import com.baeldung.graphqlreturnmap.entity.Product;
|
||||||
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
|
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -18,4 +19,12 @@ public class GraphQLQuery implements GraphQLQueryResolver {
|
||||||
return repository.getAllBooks();
|
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 {
|
type Query {
|
||||||
allBooks: [Book]
|
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 {
|
schema {
|
||||||
query: Query
|
query: Query
|
||||||
}
|
}
|
Loading…
Reference in New Issue