Skip to content

Commit f09c665

Browse files
dmfreyartembilan
authored andcommitted
GH-3501: Add GraphQL support
Fixes #3501 * make current with latest changes in 'spring-graphql' * fix checkstyle issues * implement reactive endpoint for GraphQL Query * refactor to handle GraphQL Query and Mutation requests * refactor to handle GraphQL Subscription requests * implement expressions to handle for various RequestInput parameters * convert classes to records in tests, remove unneeded datatype modifiers on channels * replace executionId with idExpression SpEL evaluator * adjust name and default expression for executionId * rename for consistency, remove unneeded null check * Clean up code style * Remove redundant variables * Add `What's New` entry * Add `package-info.java`
1 parent c661d79 commit f09c665

File tree

10 files changed

+592
-0
lines changed

10 files changed

+592
-0
lines changed

Diff for: build.gradle

+9
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ ext {
9999
smackVersion = '4.3.5'
100100
springAmqpVersion = project.hasProperty('springAmqpVersion') ? project.springAmqpVersion : '3.0.0-M1'
101101
springDataVersion = project.hasProperty('springDataVersion') ? project.springDataVersion : '2022.0.0-M1'
102+
springGraphqlVersion = '1.0.0-M5'
102103
springKafkaVersion = '3.0.0-M1'
103104
springRetryVersion = '1.3.1'
104105
springSecurityVersion = project.hasProperty('springSecurityVersion') ? project.springSecurityVersion : '6.0.0-M1'
@@ -613,6 +614,14 @@ project('spring-integration-gemfire') {
613614
}
614615
}
615616

617+
project('spring-integration-graphql') {
618+
description = 'Spring Integration GraphQL Support'
619+
dependencies {
620+
api project(':spring-integration-core')
621+
api "org.springframework.graphql:spring-graphql:$springGraphqlVersion"
622+
}
623+
}
624+
616625
project('spring-integration-groovy') {
617626
description = 'Spring Integration Groovy Support'
618627
dependencies {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.graphql.outbound;
18+
19+
import java.util.Locale;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.factory.BeanFactory;
23+
import org.springframework.expression.Expression;
24+
import org.springframework.expression.common.LiteralExpression;
25+
import org.springframework.expression.spel.support.StandardEvaluationContext;
26+
import org.springframework.graphql.GraphQlService;
27+
import org.springframework.graphql.RequestInput;
28+
import org.springframework.integration.expression.ExpressionUtils;
29+
import org.springframework.integration.expression.FunctionExpression;
30+
import org.springframework.integration.expression.SupplierExpression;
31+
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
32+
import org.springframework.lang.Nullable;
33+
import org.springframework.messaging.Message;
34+
import org.springframework.util.Assert;
35+
36+
/**
37+
* An {@link AbstractReplyProducingMessageHandler} capable of fielding
38+
* GraphQL Query, Mutation and Subscription requests.
39+
*
40+
* @author Daniel Frey
41+
*
42+
* @since 6.0
43+
*/
44+
public class GraphQlMessageHandler extends AbstractReplyProducingMessageHandler {
45+
46+
private final GraphQlService graphQlService;
47+
48+
private StandardEvaluationContext evaluationContext;
49+
50+
private Expression operationExpression;
51+
52+
private Expression operationNameExpression = new SupplierExpression<>(() -> null);
53+
54+
private Expression variablesExpression = new SupplierExpression<>(() -> null);
55+
56+
@Nullable
57+
private Locale locale;
58+
59+
private Expression executionIdExpression =
60+
new FunctionExpression<Message<?>>(message -> message.getHeaders().getId());
61+
62+
public GraphQlMessageHandler(final GraphQlService graphQlService) {
63+
Assert.notNull(graphQlService, "'graphQlService' must not be null");
64+
this.graphQlService = graphQlService;
65+
setAsync(true);
66+
}
67+
68+
/**
69+
* Specify a GraphQL Operation.
70+
* @param operation the GraphQL operation to use.
71+
*/
72+
public void setOperation(String operation) {
73+
Assert.hasText(operation, "'operation' must not be empty");
74+
setOperationExpression(new LiteralExpression(operation));
75+
}
76+
77+
/**
78+
* Specify a SpEL expression to evaluate a GraphQL Operation
79+
* @param operationExpression the expression to evaluate a GraphQL Operation.
80+
*/
81+
public void setOperationExpression(Expression operationExpression) {
82+
Assert.notNull(operationExpression, "'queryExpression' must not be null");
83+
this.operationExpression = operationExpression;
84+
}
85+
86+
/**
87+
* Set a GraphQL Operation Name to execute.
88+
* @param operationName the GraphQL Operation Name to use.
89+
*/
90+
public void setOperationName(String operationName) {
91+
setOperationNameExpression(new LiteralExpression(operationName));
92+
}
93+
94+
/**
95+
* Set a SpEL expression to evaluate a GraphQL Operation Name to execute.
96+
* @param operationNameExpression the expression to use.
97+
*/
98+
public void setOperationNameExpression(Expression operationNameExpression) {
99+
Assert.notNull(operationNameExpression, "'operationNameExpression' must not be null");
100+
this.operationNameExpression = operationNameExpression;
101+
}
102+
103+
/**
104+
* Set a SpEL expression to evaluate Variables for GraphQL Operation to execute.
105+
* @param variablesExpression the expression to use.
106+
*/
107+
public void setVariablesExpression(Expression variablesExpression) {
108+
Assert.notNull(variablesExpression, "'variablesExpression' must not be null");
109+
this.variablesExpression = variablesExpression;
110+
}
111+
112+
/**
113+
* Set a Locale for GraphQL Operation to execute.
114+
* @param locale the locale to use.
115+
*/
116+
public void setLocale(@Nullable Locale locale) {
117+
this.locale = locale;
118+
}
119+
120+
/**
121+
* Set a SpEL expression to evaluate Execution Id for GraphQL Operation Request to execute.
122+
* @param executionIdExpression the executionIdExpression to use.
123+
*/
124+
public void setExecutionIdExpression(Expression executionIdExpression) {
125+
Assert.notNull(executionIdExpression, "'executionIdExpression' must not be null");
126+
this.executionIdExpression = executionIdExpression;
127+
}
128+
129+
@Override
130+
protected final void doInit() {
131+
BeanFactory beanFactory = getBeanFactory();
132+
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory);
133+
}
134+
135+
@Override
136+
protected Object handleRequestMessage(Message<?> requestMessage) {
137+
RequestInput requestInput;
138+
139+
if (requestMessage.getPayload() instanceof RequestInput) {
140+
requestInput = (RequestInput) requestMessage.getPayload();
141+
}
142+
else {
143+
Assert.notNull(this.operationExpression, "'operationExpression' must not be null");
144+
String query = evaluateOperationExpression(requestMessage);
145+
String operationName = evaluateOperationNameExpression(requestMessage);
146+
Map<String, Object> variables = evaluateVariablesExpression(requestMessage);
147+
String id = evaluateExecutionIdExpression(requestMessage);
148+
requestInput = new RequestInput(query, operationName, variables, this.locale, id);
149+
}
150+
151+
return this.graphQlService.execute(requestInput);
152+
153+
}
154+
155+
private String evaluateOperationExpression(Message<?> message) {
156+
String operation = this.operationExpression.getValue(this.evaluationContext, message, String.class);
157+
Assert.notNull(operation, "'operationExpression' must not evaluate to null");
158+
return operation;
159+
}
160+
161+
private String evaluateOperationNameExpression(Message<?> message) {
162+
return this.operationNameExpression.getValue(this.evaluationContext, message, String.class);
163+
}
164+
165+
@SuppressWarnings("unchecked")
166+
private Map<String, Object> evaluateVariablesExpression(Message<?> message) {
167+
return this.variablesExpression.getValue(this.evaluationContext, message, Map.class);
168+
}
169+
170+
private String evaluateExecutionIdExpression(Message<?> message) {
171+
return this.executionIdExpression.getValue(this.evaluationContext, message, String.class);
172+
}
173+
174+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Provides classes for GraphQL outbound channel adapters.
3+
*/
4+
package org.springframework.integration.graphql.outbound;

0 commit comments

Comments
 (0)