JAVA-12421 Renamed graphql to graphql-modules
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
## GraphQL Domain Graph Service (DGS)
|
||||
|
||||
This module contains articles about GraphQL using the Netflix Domain Graph Service (DGS).
|
||||
|
||||
## Relevant articles:
|
||||
|
||||
- [An Introduction to Domain Graph Service (DGS) Framework](https://www.baeldung.com/spring-boot-domain-graph-service)
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>graphql-dgs</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>graphql-dgs</name>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.graphql</groupId>
|
||||
<artifactId>graphql-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.netflix.graphql.dgs</groupId>
|
||||
<artifactId>graphql-dgs-platform-dependencies</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.graphql.dgs.codegen</groupId>
|
||||
<artifactId>graphql-dgs-codegen-client-core</artifactId>
|
||||
<version>${graphql-dgs-codegen-client-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.graphql.dgs</groupId>
|
||||
<artifactId>graphql-dgs-spring-boot-starter</artifactId>
|
||||
<version>${graphql-dgs-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.github.deweyjose</groupId>
|
||||
<artifactId>graphqlcodegen-maven-plugin</artifactId>
|
||||
<version>1.14</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<schemaPaths>
|
||||
<param>src/main/resources/schema/schema.graphqls</param>
|
||||
</schemaPaths>
|
||||
<packageName>com.bealdung.graphqldgs.generated</packageName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<graphql-dgs-codegen-client-core.version>5.1.14</graphql-dgs-codegen-client-core.version>
|
||||
<graphql-dgs-spring-boot-starter.version>4.9.15</graphql-dgs-spring-boot-starter.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bealdung.graphqlDGS;
|
||||
|
||||
public class Album {
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final Integer recordNo;
|
||||
|
||||
public Album(String title, String artist, Integer recordNo) {
|
||||
this.title = title;
|
||||
this.recordNo = recordNo;
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public Integer getRecordNo() {
|
||||
return recordNo;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.bealdung.graphqlDGS;
|
||||
|
||||
import com.netflix.graphql.dgs.DgsComponent;
|
||||
import com.netflix.graphql.dgs.DgsQuery;
|
||||
import com.netflix.graphql.dgs.InputArgument;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@DgsComponent
|
||||
public class AlbumsDataFetcher {
|
||||
|
||||
private final List<Album> albums = Arrays.asList(
|
||||
new Album("Rumours", "Fleetwood Mac", 20),
|
||||
new Album("What's Going On", "Marvin Gaye", 10),
|
||||
new Album("Pet Sounds", "The Beach Boys", 12)
|
||||
);
|
||||
|
||||
@DgsQuery
|
||||
public List<Album> albums(@InputArgument String titleFilter) {
|
||||
if(titleFilter == null) {
|
||||
return albums;
|
||||
}
|
||||
return albums.stream()
|
||||
.filter(s -> s.getTitle().contains(titleFilter))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bealdung.graphqlDGS;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
server.servlet.context-path=
|
||||
@@ -0,0 +1,9 @@
|
||||
type Query {
|
||||
albums(titleFilter: String): [Album]
|
||||
}
|
||||
|
||||
type Album {
|
||||
title: String
|
||||
artist: String
|
||||
recordNo: Int
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
### Relevant Articles:
|
||||
|
||||
- [Error Handling in GraphQL With Spring Boot](https://www.baeldung.com/spring-graphql-error-handling)
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>graphql-error-handling</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>graphql-error-handling</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.graphql</groupId>
|
||||
<artifactId>graphql-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-spring-boot-starter</artifactId>
|
||||
<version>${graphql-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java-tools</artifactId>
|
||||
<version>${graphql-java-tools.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>${graphql-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
<version>${jsonassert.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<graphql-spring-boot-starter.version>5.0.2</graphql-spring-boot-starter.version>
|
||||
<graphql-java-tools.version>5.2.4</graphql-java-tools.version>
|
||||
<jsonassert.version>1.5.0</jsonassert.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.graphql.error.handling;
|
||||
|
||||
import com.baeldung.graphql.error.handling.exception.GraphQLErrorAdapter;
|
||||
import graphql.ExceptionWhileDataFetching;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.servlet.GraphQLErrorHandler;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SpringBootApplication
|
||||
public class GraphQLErrorHandlerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GraphQLErrorHandlerApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GraphQLErrorHandler errorHandler() {
|
||||
return new GraphQLErrorHandler() {
|
||||
@Override
|
||||
public List<GraphQLError> processErrors(List<GraphQLError> errors) {
|
||||
List<GraphQLError> clientErrors = errors.stream()
|
||||
.filter(this::isClientError)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<GraphQLError> serverErrors = errors.stream()
|
||||
.filter(e -> !isClientError(e))
|
||||
.map(GraphQLErrorAdapter::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<GraphQLError> e = new ArrayList<>();
|
||||
e.addAll(clientErrors);
|
||||
e.addAll(serverErrors);
|
||||
return e;
|
||||
}
|
||||
|
||||
private boolean isClientError(GraphQLError error) {
|
||||
return !(error instanceof ExceptionWhileDataFetching || error instanceof Throwable);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.graphql.error.handling.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Location {
|
||||
@Id
|
||||
private String zipcode;
|
||||
|
||||
private String city;
|
||||
private String state;
|
||||
|
||||
@OneToMany(mappedBy = "location", fetch = FetchType.EAGER)
|
||||
private List<Vehicle> vehicles = new ArrayList<>();
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.graphql.error.handling.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Vehicle {
|
||||
@Id
|
||||
private String vin;
|
||||
private Integer year;
|
||||
private String make;
|
||||
private String model;
|
||||
private String trim;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "fk_location")
|
||||
private Location location;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import graphql.ErrorType;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.language.SourceLocation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AbstractGraphQLException extends RuntimeException implements GraphQLError {
|
||||
private Map<String, Object> parameters = new HashMap();
|
||||
|
||||
public AbstractGraphQLException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AbstractGraphQLException(String message, Map<String, Object> additionParams) {
|
||||
this(message);
|
||||
if (additionParams != null) {
|
||||
parameters = additionParams;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceLocation> getLocations() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorType getErrorType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getExtensions() {
|
||||
return this.parameters;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import graphql.ErrorType;
|
||||
import graphql.ExceptionWhileDataFetching;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.language.SourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GraphQLErrorAdapter implements GraphQLError {
|
||||
|
||||
private GraphQLError error;
|
||||
|
||||
public GraphQLErrorAdapter(GraphQLError error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getExtensions() {
|
||||
return error.getExtensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceLocation> getLocations() {
|
||||
return error.getLocations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorType getErrorType() {
|
||||
return error.getErrorType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getPath() {
|
||||
return error.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> toSpecification() {
|
||||
return error.toSpecification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return (error instanceof ExceptionWhileDataFetching) ? ((ExceptionWhileDataFetching) error).getException().getMessage() : error.getMessage();
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
public class InvalidInputException extends RuntimeException {
|
||||
public InvalidInputException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class VehicleAlreadyPresentException extends AbstractGraphQLException {
|
||||
|
||||
public VehicleAlreadyPresentException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public VehicleAlreadyPresentException(String message, Map<String, Object> additionParams) {
|
||||
super(message, additionParams);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.baeldung.graphql.error.handling.exception;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class VehicleNotFoundException extends AbstractGraphQLException {
|
||||
|
||||
public VehicleNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public VehicleNotFoundException(String message, Map<String, Object> params) {
|
||||
super(message, params);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.graphql.error.handling.repository;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface InventoryRepository extends JpaRepository<Vehicle, String> {
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.graphql.error.handling.repository;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Location;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface LocationRepository extends JpaRepository<Location, String> {
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baeldung.graphql.error.handling.resolver;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Location;
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import com.baeldung.graphql.error.handling.service.InventoryService;
|
||||
import com.coxautodev.graphql.tools.GraphQLMutationResolver;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class Mutation implements GraphQLMutationResolver {
|
||||
private InventoryService inventoryService;
|
||||
|
||||
public Mutation(InventoryService inventoryService) {
|
||||
this.inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public Vehicle addVehicle(String vin, Integer year, String make, String model, String trim, Location location) {
|
||||
return this.inventoryService.addVehicle(vin, year, make, model, trim, location);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.graphql.error.handling.resolver;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import com.baeldung.graphql.error.handling.service.InventoryService;
|
||||
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class Query implements GraphQLQueryResolver {
|
||||
private final InventoryService inventoryService;
|
||||
|
||||
public Query(InventoryService inventoryService) {
|
||||
this.inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public List<Vehicle> searchAll() {
|
||||
return this.inventoryService.searchAll();
|
||||
}
|
||||
|
||||
public List<Vehicle> searchByLocation(String zipcode) {
|
||||
return this.inventoryService.searchByLocation(zipcode);
|
||||
}
|
||||
|
||||
public Vehicle searchByVin(String vin) {
|
||||
return this.inventoryService.searchByVin(vin);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.baeldung.graphql.error.handling.service;
|
||||
|
||||
import com.baeldung.graphql.error.handling.domain.Location;
|
||||
import com.baeldung.graphql.error.handling.domain.Vehicle;
|
||||
import com.baeldung.graphql.error.handling.exception.InvalidInputException;
|
||||
import com.baeldung.graphql.error.handling.exception.VehicleAlreadyPresentException;
|
||||
import com.baeldung.graphql.error.handling.exception.VehicleNotFoundException;
|
||||
import com.baeldung.graphql.error.handling.repository.InventoryRepository;
|
||||
import com.baeldung.graphql.error.handling.repository.LocationRepository;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class InventoryService {
|
||||
private InventoryRepository inventoryRepository;
|
||||
private LocationRepository locationRepository;
|
||||
|
||||
public InventoryService(InventoryRepository inventoryRepository, LocationRepository locationRepository) {
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.locationRepository = locationRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Vehicle addVehicle(String vin, Integer year, String make, String model, String trim, Location location) {
|
||||
Optional<Vehicle> existingVehicle = this.inventoryRepository.findById(vin);
|
||||
if (existingVehicle.isPresent()) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("vin", vin);
|
||||
throw new VehicleAlreadyPresentException("Failed to add vehicle. Vehicle with vin " + vin + " already present.", params);
|
||||
}
|
||||
Vehicle vehicle = Vehicle.builder()
|
||||
.vin(vin)
|
||||
.year(year)
|
||||
.make(make)
|
||||
.model(model)
|
||||
.location(location)
|
||||
.trim(trim)
|
||||
.build();
|
||||
|
||||
this.locationRepository.save(location);
|
||||
return this.inventoryRepository.save(vehicle);
|
||||
}
|
||||
|
||||
public List<Vehicle> searchAll() {
|
||||
return this.inventoryRepository.findAll();
|
||||
}
|
||||
|
||||
public List<Vehicle> searchByLocation(String zipcode) {
|
||||
if (StringUtils.isEmpty(zipcode) || zipcode.length() != 5) {
|
||||
throw new InvalidInputException("Invalid zipcode " + zipcode + " provided.");
|
||||
}
|
||||
return this.locationRepository.findById(zipcode)
|
||||
.map(Location::getVehicles)
|
||||
.orElse(new ArrayList<>());
|
||||
}
|
||||
|
||||
public Vehicle searchByVin(String vin) {
|
||||
return this.inventoryRepository.findById(vin).orElseThrow(() -> {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("vin", vin);
|
||||
return new VehicleNotFoundException("Vehicle with vin " + vin + " not found.", params);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
graphql:
|
||||
servlet:
|
||||
mapping: /graphql
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: "jdbc:h2:mem:graphqldb"
|
||||
driverClassName: "org.h2.Driver"
|
||||
username: sa
|
||||
password:
|
||||
|
||||
initialization-mode: always
|
||||
platform: h2
|
||||
|
||||
jpa:
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.H2Dialect
|
||||
ddl-auto: none
|
||||
|
||||
h2:
|
||||
console.enabled: true
|
||||
@@ -0,0 +1,23 @@
|
||||
type Vehicle {
|
||||
vin: ID!
|
||||
year: Int!
|
||||
make: String!
|
||||
model: String!
|
||||
trim: String!
|
||||
}
|
||||
|
||||
input Location {
|
||||
city: String
|
||||
state: String
|
||||
zipcode: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
searchAll: [Vehicle]!
|
||||
searchByLocation(zipcode: String!): [Vehicle]!
|
||||
searchByVin(vin: String!): Vehicle
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
addVehicle(vin: ID!, year: Int!, make: String!, model: String!, trim: String, location: Location): Vehicle!
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
insert into LOCATION values('07092', 'Mountainside', 'NJ');
|
||||
insert into LOCATION values ('94118', 'San Francisco', 'CA');
|
||||
insert into LOCATION values ('10002', 'New York', 'NY');
|
||||
|
||||
insert into VEHICLE (vin, year, make, model, trim, fk_location) values('KM8JN72DX7U587496', 2007, 'Hyundai', 'Tucson', null, '07092');
|
||||
insert into VEHICLE (vin, year, make, model, trim, fk_location) values('JTKKU4B41C1023346', 2012, 'Toyota', 'Scion', 'Xd', '94118');
|
||||
insert into VEHICLE (vin, year, make, model, trim, fk_location) values('1G1JC1444PZ215071', 2000, 'Chevrolet', 'CAVALIER VL', 'RS', '07092');
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.baeldung.graphql.error.handling;
|
||||
|
||||
import com.graphql.spring.boot.test.GraphQLResponse;
|
||||
import com.graphql.spring.boot.test.GraphQLTestTemplate;
|
||||
import org.json.JSONException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.baeldung.graphql.error.handling.TestUtils.readFile;
|
||||
import static java.lang.String.format;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = GraphQLErrorHandlerApplication.class)
|
||||
public class GraphQLErrorHandlerApplicationIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private GraphQLTestTemplate graphQLTestTemplate;
|
||||
|
||||
private static final String GRAPHQL_TEST_REQUEST_PATH = "graphql/request/%s.graphql";
|
||||
private static final String GRAPHQL_TEST_RESPONSE_PATH = "graphql/response/%s.json";
|
||||
|
||||
@Test
|
||||
public void whenUnknownOperation_thenRespondWithRequestError() throws IOException, JSONException {
|
||||
String graphqlName = "request_error_unknown_operation";
|
||||
GraphQLResponse actualResponse = graphQLTestTemplate.postForResource(format(GRAPHQL_TEST_REQUEST_PATH, graphqlName));
|
||||
String expectedResponse = readFile(format(GRAPHQL_TEST_RESPONSE_PATH, graphqlName));
|
||||
|
||||
JSONAssert.assertEquals(expectedResponse, actualResponse.getRawResponse().getBody(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenInvalidSyntaxRequest_thenRespondWithRequestError() throws IOException, JSONException {
|
||||
String graphqlName = "request_error_invalid_request_syntax";
|
||||
GraphQLResponse actualResponse = graphQLTestTemplate.postForResource(format(GRAPHQL_TEST_REQUEST_PATH, graphqlName));
|
||||
String expectedResponse = readFile(format(GRAPHQL_TEST_RESPONSE_PATH, graphqlName));
|
||||
|
||||
JSONAssert.assertEquals(expectedResponse, actualResponse.getRawResponse().getBody(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestAllNonNullField_thenRespondPartialDataWithFieldError() throws IOException, JSONException {
|
||||
String graphqlName = "field_error_request_non_null_fields_partial_response";
|
||||
GraphQLResponse actualResponse = graphQLTestTemplate.postForResource(format(GRAPHQL_TEST_REQUEST_PATH, graphqlName));
|
||||
String expectedResponse = readFile(format(GRAPHQL_TEST_RESPONSE_PATH, graphqlName));
|
||||
|
||||
JSONAssert.assertEquals(expectedResponse, actualResponse.getRawResponse().getBody(), true);
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.baeldung.graphql.error.handling;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class TestUtils {
|
||||
public static String readFile(String path) throws IOException {
|
||||
return IOUtils.toString(
|
||||
new ClassPathResource(path).getInputStream(), Charset.defaultCharset()
|
||||
);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# trim is non null but one of the record has null value for trim
|
||||
query {
|
||||
searchAll {
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
trim
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
query {
|
||||
searchByVin(vin: "error) {
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
trim
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
subscription {
|
||||
searchByVin(vin: "75024") {
|
||||
vin
|
||||
year
|
||||
make
|
||||
model
|
||||
trim
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"data": {
|
||||
"searchAll": [
|
||||
null,
|
||||
{
|
||||
"vin": "JTKKU4B41C1023346",
|
||||
"year": 2012,
|
||||
"make": "Toyota",
|
||||
"model": "Scion",
|
||||
"trim": "Xd"
|
||||
},
|
||||
{
|
||||
"vin": "1G1JC1444PZ215071",
|
||||
"year": 2000,
|
||||
"make": "Chevrolet",
|
||||
"model": "CAVALIER VL",
|
||||
"trim": "RS"
|
||||
}
|
||||
]
|
||||
},
|
||||
"errors": [
|
||||
{
|
||||
"message": "Cannot return null for non-nullable type: 'String' within parent 'Vehicle' (/searchAll[0]/trim)",
|
||||
"path": [
|
||||
"searchAll",
|
||||
0,
|
||||
"trim"
|
||||
],
|
||||
"errorType": "DataFetchingException",
|
||||
"locations": null,
|
||||
"extensions": null
|
||||
}
|
||||
]
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": null,
|
||||
"errors": [
|
||||
{
|
||||
"message": "Invalid Syntax",
|
||||
"locations": [
|
||||
{
|
||||
"line": 5,
|
||||
"column": 8,
|
||||
"sourceName": null
|
||||
}
|
||||
],
|
||||
"errorType": "InvalidSyntax",
|
||||
"path": null,
|
||||
"extensions": null
|
||||
}
|
||||
]
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": null,
|
||||
"errors": [
|
||||
{
|
||||
"errorType": "OperationNotSupported",
|
||||
"locations": [
|
||||
{
|
||||
"line": 1,
|
||||
"column": 1,
|
||||
"sourceName": null
|
||||
}
|
||||
],
|
||||
"extensions": null,
|
||||
"message": "Schema is not configured for subscriptions.",
|
||||
"path": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
## GraphQL Java
|
||||
|
||||
This module contains articles about GraphQL with Java
|
||||
|
||||
## Relevant articles:
|
||||
|
||||
- [Introduction to GraphQL](https://www.baeldung.com/graphql)
|
||||
- [Make a Call to a GraphQL Service from a Java Application](https://www.baeldung.com/java-call-graphql-service)
|
||||
- [Return Map from GraphQL](https://www.baeldung.com/java-graphql-return-map)
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"query": "mutation($name: String! $email: String! $age: String! ){ createUser ( name: $name email: $email age: $age) { id name email age } }",
|
||||
"parameters": {
|
||||
"name": "John",
|
||||
"email": "john@email.com",
|
||||
"age": 34
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"query": "mutation($name: String! ){ deleteUser ( id: $id) { id name email age} }",
|
||||
"parameters": {
|
||||
"id": 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"query": "{ listUsers{ id name email age}}",
|
||||
"parameters": {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"query": "query($id: String!){ retrieveUser ( id: $id) { id name email} }",
|
||||
"parameters": {
|
||||
"id": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"query": "query($id: String!){ searchName ( id: $id) { id name email} }",
|
||||
"parameters": {
|
||||
"id": 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"query": "mutation($id: String! $name: String! $email: String! $age: String! ){ updateUser ( id: $id name: $name email: $email age: $age) { id name email age} }",
|
||||
"parameters": {
|
||||
"id": 1,
|
||||
"name":"John updated",
|
||||
"email": "johnupdate@email.com",
|
||||
"age": 50
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>graphql-java</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>graphql-java</name>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.graphql</groupId>
|
||||
<artifactId>graphql-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>central</id>
|
||||
<name>Central Repository</name>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<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>${nodes.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java</artifactId>
|
||||
<version>${graphql-java.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java-tools</artifactId>
|
||||
<version>${graphql-java-tools.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java-servlet</artifactId>
|
||||
<version>${graphql-java-servlet.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java-generator</groupId>
|
||||
<artifactId>graphql-java-runtime</artifactId>
|
||||
<version>${graphql.java.generator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${javax.servlet-api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${httpclient.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-netty</artifactId>
|
||||
<version>${mockserver-netty.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-client-java</artifactId>
|
||||
<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>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>${jetty-maven-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>${maven-war-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.graphql-java-generator</groupId>
|
||||
<artifactId>graphql-maven-plugin</artifactId>
|
||||
<version>${graphql.java.generator.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>generateClientCode</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<packageName>com.baeldung.graphql.generated</packageName>
|
||||
<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>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<graphql-java.version>11.0</graphql-java.version>
|
||||
<graphql-java-tools.version>5.2.4</graphql-java-tools.version>
|
||||
<graphql-java-servlet.version>6.1.3</graphql-java-servlet.version>
|
||||
<graphql-java-annotations.version>3.0.3</graphql-java-annotations.version>
|
||||
<graphql.java.generator.version>1.18</graphql.java.generator.version>
|
||||
<ratpack-core.version>1.9.0</ratpack-core.version>
|
||||
<nodes.version>0.5.0</nodes.version>
|
||||
<httpclient.version>4.5.13</httpclient.version>
|
||||
<mockserver-netty.version>5.13.2</mockserver-netty.version>
|
||||
<mockserver-client-java.version>5.13.2</mockserver-client-java.version>
|
||||
<jetty-maven-plugin.version>10.0.7</jetty-maven-plugin.version>
|
||||
<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>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baeldung.graphql;
|
||||
|
||||
import ratpack.server.RatpackServer;
|
||||
|
||||
import com.baeldung.graphql.handler.UserHandler;
|
||||
|
||||
public class Application {
|
||||
public static void main(String[] args) throws Exception {
|
||||
new Application();
|
||||
}
|
||||
|
||||
private Application() throws Exception {
|
||||
final RatpackServer server = RatpackServer.of(s -> s.handlers(chain -> chain.post("users", new UserHandler())));
|
||||
server.start();
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -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<Data> 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);
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -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<Book> books = Stream.of(
|
||||
new Book("Title 1", new Author("Pero", "Peric")),
|
||||
new Book("Title 2", new Author("Marko", "Maric"))
|
||||
).collect(Collectors.toList());
|
||||
|
||||
public List<Book> getAllBooks() {
|
||||
return Collections.unmodifiableList(books);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.baeldung.graphql.data;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Data {
|
||||
|
||||
private List<Book> allBooks;
|
||||
|
||||
public Data() {
|
||||
|
||||
}
|
||||
|
||||
public Data(List<Book> allBooks) {
|
||||
this.allBooks = allBooks;
|
||||
}
|
||||
|
||||
public List<Book> getAllBooks() {
|
||||
return Collections.unmodifiableList(allBooks);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.baeldung.graphql.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.baeldung.graphql.handler.UserHandler;
|
||||
import com.baeldung.graphql.utils.SchemaUtils;
|
||||
|
||||
import graphql.annotations.GraphQLField;
|
||||
import graphql.annotations.GraphQLName;
|
||||
|
||||
@GraphQLName(SchemaUtils.USER)
|
||||
public class User {
|
||||
|
||||
@GraphQLField
|
||||
private Long id;
|
||||
@GraphQLField
|
||||
private String name;
|
||||
@GraphQLField
|
||||
private String email;
|
||||
@GraphQLField
|
||||
private Integer age;
|
||||
|
||||
public User(String name, String email, Integer age) {
|
||||
this.id = genId();
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public static Long genId() {
|
||||
Long id = 1L;
|
||||
try {
|
||||
List<User> users = new UserHandler().getUsers();
|
||||
for (User user : users)
|
||||
id = (user.getId() > id ? user.getId() : id) + 1;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[id=" + id + ", name=" + name + ", email="+email+ ", age="+ age +"]";
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.baeldung.graphql.handler;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import ratpack.handling.Context;
|
||||
import ratpack.handling.Handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.baeldung.graphql.entity.User;
|
||||
import com.baeldung.graphql.schema.UserSchema;
|
||||
import com.baeldung.graphql.utils.SchemaUtils;
|
||||
|
||||
import static ratpack.jackson.Jackson.json;
|
||||
|
||||
public class UserHandler implements Handler {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(UserHandler.class.getSimpleName());
|
||||
|
||||
private static final List<User> USERS = new ArrayList<>();
|
||||
|
||||
private final GraphQL graphql;
|
||||
|
||||
public UserHandler() throws Exception {
|
||||
graphql = GraphQL.newGraphQL(new UserSchema().getSchema()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Context context) {
|
||||
context.parse(Map.class)
|
||||
.then(payload -> {
|
||||
Map<String, Object> parameters = (Map<String, Object>) payload.get("parameters");
|
||||
ExecutionResult executionResult = graphql.execute(payload.get(SchemaUtils.QUERY)
|
||||
.toString(), null, this, parameters);
|
||||
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return USERS;
|
||||
}
|
||||
|
||||
public static List<User> getUsers(DataFetchingEnvironment env) {
|
||||
return ((UserHandler) env.getSource()).getUsers();
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.baeldung.graphql.mutation;
|
||||
|
||||
import graphql.annotations.GraphQLField;
|
||||
import graphql.annotations.GraphQLName;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.baeldung.graphql.entity.User;
|
||||
import com.baeldung.graphql.utils.SchemaUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.baeldung.graphql.handler.UserHandler.getUsers;
|
||||
|
||||
@GraphQLName(SchemaUtils.MUTATION)
|
||||
public class UserMutation {
|
||||
@GraphQLField
|
||||
public static User createUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.NAME) final String name, @NotNull @GraphQLName(SchemaUtils.EMAIL) final String email, @NotNull @GraphQLName(SchemaUtils.AGE) final String age) {
|
||||
List<User> users = getUsers(env);
|
||||
final User user = new User(name, email, Integer.valueOf(age));
|
||||
users.add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@GraphQLField
|
||||
public static User updateUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.ID) final String id, @NotNull @GraphQLName(SchemaUtils.NAME) final String name, @NotNull @GraphQLName(SchemaUtils.EMAIL) final String email,
|
||||
@NotNull @GraphQLName(SchemaUtils.AGE) final String age) {
|
||||
final Optional<User> user = getUsers(env).stream()
|
||||
.filter(c -> c.getId() == Long.parseLong(id))
|
||||
.findFirst();
|
||||
if (!user.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
user.get()
|
||||
.setName(name);
|
||||
user.get()
|
||||
.setEmail(email);
|
||||
user.get()
|
||||
.setAge(Integer.valueOf(age));
|
||||
return user.get();
|
||||
}
|
||||
|
||||
@GraphQLField
|
||||
public static User deleteUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.ID) final String id) {
|
||||
final List<User> users = getUsers(env);
|
||||
final Optional<User> user = users.stream()
|
||||
.filter(c -> c.getId() == Long.parseLong(id))
|
||||
.findFirst();
|
||||
if (!user.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
users.removeIf(c -> c.getId() == Long.parseLong(id));
|
||||
return user.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.baeldung.graphql.query;
|
||||
|
||||
import graphql.annotations.GraphQLField;
|
||||
import graphql.annotations.GraphQLName;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.baeldung.graphql.entity.User;
|
||||
import com.baeldung.graphql.utils.SchemaUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.baeldung.graphql.handler.UserHandler.getUsers;
|
||||
|
||||
@GraphQLName(SchemaUtils.QUERY)
|
||||
public class UserQuery {
|
||||
|
||||
@GraphQLField
|
||||
public static User retrieveUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.ID) final String id) {
|
||||
final Optional<User> any = getUsers(env).stream()
|
||||
.filter(c -> c.getId() == Long.parseLong(id))
|
||||
.findFirst();
|
||||
return any.orElse(null);
|
||||
}
|
||||
|
||||
@GraphQLField
|
||||
public static List<User> searchName(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.NAME) final String name) {
|
||||
return getUsers(env).stream()
|
||||
.filter(c -> c.getName()
|
||||
.contains(name))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GraphQLField
|
||||
public static List<User> listUsers(final DataFetchingEnvironment env) {
|
||||
return getUsers(env);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.baeldung.graphql.schema;
|
||||
|
||||
import graphql.annotations.GraphQLAnnotations;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
|
||||
import static graphql.schema.GraphQLSchema.newSchema;
|
||||
|
||||
import com.baeldung.graphql.mutation.UserMutation;
|
||||
import com.baeldung.graphql.query.UserQuery;
|
||||
|
||||
public class UserSchema {
|
||||
|
||||
private final GraphQLSchema schema;
|
||||
|
||||
public UserSchema() throws IllegalAccessException, NoSuchMethodException, InstantiationException {
|
||||
schema = newSchema().query(GraphQLAnnotations.object(UserQuery.class))
|
||||
.mutation(GraphQLAnnotations.object(UserMutation.class))
|
||||
.build();
|
||||
}
|
||||
|
||||
public GraphQLSchema getSchema() {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
+36
@@ -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();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
|
||||
public class GraphQLQuery implements GraphQLQueryResolver {
|
||||
|
||||
private final BookRepository repository;
|
||||
|
||||
public GraphQLQuery(BookRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public List<Book> allBooks() {
|
||||
return repository.getAllBooks();
|
||||
}
|
||||
|
||||
public List<Product> getProducts(int pageSize, int pageNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Product getProduct(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.baeldung.graphql.utils;
|
||||
|
||||
public class SchemaUtils {
|
||||
public static final String USER = "user";
|
||||
public static final String ID = "id";
|
||||
public static final String NAME = "name";
|
||||
public static final String EMAIL = "email";
|
||||
public static final String AGE = "age";
|
||||
|
||||
public static final String MUTATION = "mutation";
|
||||
public static final String QUERY = "query";
|
||||
public static final String ERRORS = "errors";
|
||||
public static final String DATA = "data";
|
||||
}
|
||||
+52
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
+24
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
+48
@@ -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;
|
||||
}
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
|
||||
}
|
||||
+43
@@ -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);
|
||||
}
|
||||
}
|
||||
+30
@@ -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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,43 @@
|
||||
type Book {
|
||||
title: String!
|
||||
author: Author
|
||||
}
|
||||
|
||||
type Author {
|
||||
name: String!
|
||||
surname: String!
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.baeldung.graphql;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.mockserver.client.MockServerClient;
|
||||
import org.mockserver.configuration.Configuration;
|
||||
import org.mockserver.integration.ClientAndServer;
|
||||
import org.mockserver.model.HttpStatusCode;
|
||||
import org.slf4j.event.Level;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
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 {
|
||||
|
||||
private static final String SERVER_ADDRESS = "127.0.0.1";
|
||||
private static final String PATH = "/graphql";
|
||||
|
||||
public static String serviceUrl;
|
||||
|
||||
private static ClientAndServer mockServer;
|
||||
private static int serverPort;
|
||||
|
||||
@BeforeAll
|
||||
static void startServer() throws IOException {
|
||||
serverPort = getFreePort();
|
||||
serviceUrl = "http://" + SERVER_ADDRESS + ":" + serverPort + PATH;
|
||||
|
||||
Configuration config = Configuration.configuration().logLevel(Level.WARN);
|
||||
mockServer = startClientAndServer(config, 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -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<Data> 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<Data> 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")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -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")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
### Relevant Articles:
|
||||
|
||||
- [Getting Started With GraphQL SPQR and Spring Boot](https://www.baeldung.com/spring-boot-graphql-spqr)
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>graphql-spqr</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>graphql-spqr</name>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.graphql</groupId>
|
||||
<artifactId>graphql-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.leangen.graphql</groupId>
|
||||
<artifactId>graphql-spqr-spring-boot-starter</artifactId>
|
||||
<version>${graphql-spqr-spring-boot-starter-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<graphql-spqr-spring-boot-starter-version>0.0.6</graphql-spqr-spring-boot-starter-version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.baeldung;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringBootApp {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringBootApp.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.baeldung.spqr;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.baeldung.spqr;
|
||||
|
||||
import io.leangen.graphql.annotations.GraphQLArgument;
|
||||
import io.leangen.graphql.annotations.GraphQLMutation;
|
||||
import io.leangen.graphql.annotations.GraphQLQuery;
|
||||
import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@GraphQLApi
|
||||
public class BookService implements IBookService {
|
||||
|
||||
Set<Book> books = new HashSet<>();
|
||||
|
||||
@GraphQLQuery(name = "getBookWithTitle")
|
||||
public Book getBookWithTitle(@GraphQLArgument(name = "title") String title) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getTitle()
|
||||
.equals(title))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@GraphQLQuery(name = "getAllBooks", description = "Get all books")
|
||||
public List<Book> getAllBooks() {
|
||||
return books.stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GraphQLMutation(name = "addBook")
|
||||
public Book addBook(@GraphQLArgument(name = "newBook") Book book) {
|
||||
books.add(book);
|
||||
return book;
|
||||
}
|
||||
|
||||
@GraphQLMutation(name = "updateBook")
|
||||
public Book updateBook(@GraphQLArgument(name = "modifiedBook") Book book) {
|
||||
books.remove(book);
|
||||
books.add(book);
|
||||
return book;
|
||||
}
|
||||
|
||||
@GraphQLMutation(name = "deleteBook")
|
||||
public boolean deleteBook(@GraphQLArgument(name = "book") Book book) {
|
||||
return books.remove(book);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.baeldung.spqr;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IBookService {
|
||||
Book getBookWithTitle(String title);
|
||||
|
||||
List<Book> getAllBooks();
|
||||
|
||||
Book addBook(Book book);
|
||||
|
||||
Book updateBook(Book book);
|
||||
|
||||
boolean deleteBook(Book book);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung.graphql</groupId>
|
||||
<artifactId>graphql-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>graphql-modules</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>graphql-dgs</module>
|
||||
<module>graphql-error-handling</module>
|
||||
<module>graphql-java</module>
|
||||
<module>graphql-spqr</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user