Skip to content

Commit 7ab8cc8

Browse files
authored
Fix GraphQL query serialization and add Gradle plugin for client-codegen #37 (#59)
* Introduce codegen for client code - Part 1. #37 * Remove redundant null-checks * Minor fixes in Request class generation + improve code coverage. #37 * Customizable suffix of ResponseProjection classes. #37 * Code coverage for MappingConfig #37 * Fix method names #37 #53 * Add codegen properties to GraphqlCodegenGradleTask #37 * Rename example module to example-server #37 * Change example-server project for gradle plugin #37 * Add GraphQLResult class which holds response data+errors #37 * Fix GraphQLRequestSerializer #37 * Add example-client project for gradle plugin #37 * Add example-client project to CI and README.md #37 * Code coverage for GraphQLResult #37 * Downgrade all dependencies level to 'implementation' #37 * Fix GraphQL query characters escape + fix errors serialization #37 * Fix GraphQL request serialization #37 * Fix toString() generation for request input classes #37 * Remove redundant apache-commons dependency #37 * Use jackson lib to construct GraphQL query json #37
1 parent ce90069 commit 7ab8cc8

File tree

101 files changed

+1416
-415
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+1416
-415
lines changed

.circleci/config.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,11 @@ jobs:
9696
name: Build plugin and run unit tests
9797
command: cd ~/repo/plugins/gradle/graphql-java-codegen-gradle-plugin && gradle build publishToMavenLocal
9898
- run:
99-
name: Build example plugin project
100-
command: cd ~/repo/plugins/gradle/example && gradle test
99+
name: Build example-server plugin project
100+
command: cd ~/repo/plugins/gradle/example-server && gradle test
101+
- run:
102+
name: Build example-client plugin project
103+
command: cd ~/repo/plugins/gradle/example-client && gradle test
101104
workflows:
102105
build-library-and-plugins:
103106
jobs:

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ Please follow the steps below in order to make the changes:
5050
mvn clean install
5151
```
5252

53-
7. Make sure that `example` project is compiling and running.
53+
7. Make sure that `example` projects are compiling and running.

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ repositories {
1717
}
1818

1919
dependencies {
20-
compile "org.freemarker:freemarker:2.3.28"
21-
compile "com.graphql-java:graphql-java:13.0"
22-
compile "com.google.code.gson:gson:2.8.6"
20+
implementation "org.freemarker:freemarker:2.3.28"
21+
implementation "com.graphql-java:graphql-java:13.0"
22+
implementation "com.fasterxml.jackson.core:jackson-databind:2.9.9"
2323

24-
compileOnly "org.projectlombok:lombok:1.18.8"
24+
implementation "org.projectlombok:lombok:1.18.8"
2525
annotationProcessor "org.projectlombok:lombok:1.18.8"
2626

2727
testImplementation "org.junit.jupiter:junit-jupiter-api:5.5.1"

plugins/gradle/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ Access to classes from your code as normal Kotlin classes.
171171

172172
### Example
173173

174-
[example](example)
174+
* GraphQL server code generation: [example-server](example-server)
175+
* GraphQL client code generation: [example-client](example-client)
175176

176177

177178
### Inspired by
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import io.github.kobylynskyi.graphql.codegen.gradle.GraphqlCodegenGradleTask
2+
3+
plugins {
4+
id "java"
5+
id "idea"
6+
id "application"
7+
id "net.ltgt.apt" version "0.20"
8+
id "io.github.kobylynskyi.graphql.codegen" version "1.6.0-SNAPSHOT"
9+
}
10+
11+
mainClassName = "io.github.kobylynskyi.bikeshop.Application"
12+
13+
dependencies {
14+
compile "org.springframework.boot:spring-boot-starter-web:2.1.7.RELEASE"
15+
compile "org.springframework.boot:spring-boot-starter-data-mongodb:2.1.7.RELEASE"
16+
compile "org.springframework.data:spring-data-commons:2.1.10.RELEASE"
17+
18+
compile "com.graphql-java-kickstart:graphql-java-tools:5.6.1"
19+
compile "com.graphql-java-kickstart:graphql-spring-boot-starter:5.10.0"
20+
compile "com.graphql-java-kickstart:graphiql-spring-boot-starter:5.10.0"
21+
22+
compile "io.github.kobylynskyi:graphql-java-codegen:1.6.0-SNAPSHOT"
23+
24+
compile "org.apache.httpcomponents:httpclient:4.5.12"
25+
26+
implementation "org.mapstruct:mapstruct:1.3.0.Final"
27+
annotationProcessor "org.mapstruct:mapstruct-processor:1.3.0.Final"
28+
29+
compileOnly "org.projectlombok:lombok:1.18.8"
30+
annotationProcessor "org.projectlombok:lombok:1.18.8"
31+
}
32+
33+
/**
34+
* Generate requests and model from external service
35+
*/
36+
compileJava.dependsOn "graphqlCodegenProductService"
37+
sourceSets.main.java.srcDirs "$buildDir/generated-client"
38+
task graphqlCodegenProductService(type: GraphqlCodegenGradleTask) {
39+
graphqlSchemaPaths = ["$projectDir/src/main/resources/external/schema-product-service.graphqls".toString()]
40+
outputDir = new File("$buildDir/generated-client")
41+
modelPackageName = "io.github.kobylynskyi.product.graphql.model"
42+
customTypesMapping = [
43+
DateTime: "java.util.Date"
44+
]
45+
modelNameSuffix = "TO"
46+
generateRequests = true
47+
generateApis = false
48+
}
49+
50+
/**
51+
* Generate apis and model
52+
*/
53+
compileJava.dependsOn "graphqlCodegenOrderService"
54+
sourceSets.main.java.srcDirs "$buildDir/generated-server"
55+
task graphqlCodegenOrderService(type: GraphqlCodegenGradleTask) {
56+
graphqlSchemaPaths = ["$projectDir/src/main/resources/schema.graphqls".toString()]
57+
outputDir = new File("$buildDir/generated-server")
58+
apiPackageName = "io.github.kobylynskyi.order.graphql.api"
59+
modelPackageName = "io.github.kobylynskyi.order.graphql.model"
60+
customTypesMapping = [
61+
DateTime: "java.util.Date"
62+
]
63+
modelNameSuffix = "TO"
64+
}
65+
66+
repositories {
67+
jcenter()
68+
mavenCentral()
69+
mavenLocal()
70+
}

plugins/gradle/example/settings.gradle renamed to plugins/gradle/example-client/settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ pluginManagement {
55
}
66
}
77

8-
rootProject.name = "example"
8+
rootProject.name = "example-client"

plugins/gradle/example/src/main/java/io/github/kobylynskyi/bikeshop/Application.java renamed to plugins/gradle/example-client/src/main/java/io/github/kobylynskyi/order/Application.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.kobylynskyi.bikeshop;
1+
package io.github.kobylynskyi.order;
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.github.kobylynskyi.order.external;
2+
3+
import io.github.kobylynskyi.order.model.Product;
4+
import io.github.kobylynskyi.product.graphql.model.ProductTO;
5+
import org.mapstruct.Mapper;
6+
7+
@Mapper(componentModel = "spring")
8+
public interface ExternalProductMapper {
9+
10+
Product map(ProductTO from);
11+
12+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.github.kobylynskyi.order.external;
2+
3+
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLRequest;
4+
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLResult;
5+
import io.github.kobylynskyi.order.model.Product;
6+
import io.github.kobylynskyi.order.model.UnableToRetrieveProductException;
7+
import io.github.kobylynskyi.product.graphql.model.ProductByIdQueryRequest;
8+
import io.github.kobylynskyi.product.graphql.model.ProductResponseProjection;
9+
import io.github.kobylynskyi.product.graphql.model.ProductTO;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.core.ParameterizedTypeReference;
13+
import org.springframework.http.HttpEntity;
14+
import org.springframework.http.HttpHeaders;
15+
import org.springframework.http.HttpMethod;
16+
import org.springframework.http.MediaType;
17+
import org.springframework.stereotype.Service;
18+
import org.springframework.web.client.RestTemplate;
19+
20+
import java.net.URI;
21+
import java.util.Collections;
22+
import java.util.Map;
23+
24+
@Service
25+
public class ProductServiceGraphQLClient {
26+
27+
@Autowired
28+
private ExternalProductMapper productMapper;
29+
30+
@Autowired
31+
private RestTemplate restTemplate;
32+
33+
@Value("${external.service.product.url}")
34+
private String productUrl;
35+
36+
public Product getProduct(String productId) throws UnableToRetrieveProductException {
37+
ProductByIdQueryRequest getProductRequest = new ProductByIdQueryRequest();
38+
getProductRequest.setId(productId);
39+
GraphQLRequest request = new GraphQLRequest(getProductRequest,
40+
new ProductResponseProjection()
41+
.id()
42+
.title()
43+
.price());
44+
45+
GraphQLResult<Map<String, ProductTO>> result = restTemplate.exchange(URI.create(productUrl),
46+
HttpMethod.POST,
47+
httpEntity(request),
48+
new ParameterizedTypeReference<GraphQLResult<Map<String, ProductTO>>>() {
49+
}).getBody();
50+
assert result != null;
51+
if (result.hasErrors()) {
52+
throw new UnableToRetrieveProductException(productId, result.getErrors().get(0).getMessage());
53+
}
54+
return productMapper.map(result.getData().get(getProductRequest.getOperationName()));
55+
}
56+
57+
private static HttpEntity<String> httpEntity(Object request) {
58+
HttpHeaders headers = new HttpHeaders();
59+
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));
60+
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
61+
return new HttpEntity<>(request.toString(), headers);
62+
}
63+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.github.kobylynskyi.order.external.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
6+
import org.springframework.web.client.RestTemplate;
7+
8+
@Configuration
9+
public class RestClientsConfiguration {
10+
11+
@Bean
12+
public RestTemplate restTemplate() {
13+
return new RestTemplate(new HttpComponentsClientHttpRequestFactory());
14+
}
15+
16+
}

0 commit comments

Comments
 (0)