Skip to content

Commit bb44a34

Browse files
authored
fix(GH-233): Support Inline optional search criteria for singular and plural attributes (#251)
* fix: add ON condition on the right side of collection join fetch criteria * fix: remove Hibernate HqlSqlWalker hack * fix: add GraphQLJpaOneToManyDataFetcher for inline query filter * fix: optimize OneToManyDataFetcher using query stream result * fix: update name to GraphQLJpaOneToManyDataFetcher * feat: add OneToMany batch DataLoader support * fix: refactor GraphQLJpaOneToManyMappedBatchLoader * feat: add GraphQLJpaToOneMappedBatchLoader support * fix: add batch query logging * fix: add support orderBy sort for collections * fix: unexpected NPE in getJPQLQueryString * fix: add support for inline optional to one assiciations * fix: add inline optional support for singular attributes * fix: add support for inline collections filter with alias * fix: remove hibernate-core dependency * fix: polish data loader data fetchers
1 parent 4697268 commit bb44a34

14 files changed

+2612
-1501
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.introproventures.graphql.jpa.query.schema.impl;
2+
3+
import java.util.LinkedHashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
7+
import org.dataloader.DataLoader;
8+
import org.dataloader.DataLoaderOptions;
9+
import org.dataloader.DataLoaderRegistry;
10+
import org.dataloader.MappedBatchLoaderWithContext;
11+
12+
public class BatchLoaderRegistry {
13+
private final static Map<String, MappedBatchLoaderWithContext<Object, List<Object>>> mappedToManyBatchLoaders = new LinkedHashMap<>();
14+
private final static Map<String, MappedBatchLoaderWithContext<Object, Object>> mappedToOneBatchLoaders = new LinkedHashMap<>();
15+
private static BatchLoaderRegistry instance = new BatchLoaderRegistry();
16+
17+
public static BatchLoaderRegistry getInstance() {
18+
return instance;
19+
}
20+
21+
public static void registerToMany(String batchLoaderKey, MappedBatchLoaderWithContext<Object, List<Object>> mappedBatchLoader) {
22+
mappedToManyBatchLoaders.putIfAbsent(batchLoaderKey, mappedBatchLoader);
23+
}
24+
25+
public static void registerToOne(String batchLoaderKey, MappedBatchLoaderWithContext<Object, Object> mappedBatchLoader) {
26+
mappedToOneBatchLoaders.putIfAbsent(batchLoaderKey, mappedBatchLoader);
27+
}
28+
29+
public static DataLoaderRegistry newDataLoaderRegistry(DataLoaderOptions dataLoaderOptions) {
30+
DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
31+
32+
mappedToManyBatchLoaders.entrySet()
33+
.forEach(entry -> {
34+
DataLoader<Object, List<Object>> dataLoader = DataLoader.newMappedDataLoader(entry.getValue(),
35+
dataLoaderOptions);
36+
dataLoaderRegistry.register(entry.getKey(), dataLoader);
37+
});
38+
39+
mappedToOneBatchLoaders.entrySet()
40+
.forEach(entry -> {
41+
DataLoader<Object, Object> dataLoader = DataLoader.newMappedDataLoader(entry.getValue(),
42+
dataLoaderOptions);
43+
dataLoaderRegistry.register(entry.getKey(), dataLoader);
44+
});
45+
46+
return dataLoaderRegistry;
47+
48+
}
49+
50+
}

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContext.java

+72-10
Original file line numberDiff line numberDiff line change
@@ -16,55 +16,98 @@
1616

1717
package com.introproventures.graphql.jpa.query.schema.impl;
1818

19+
import java.util.Arrays;
20+
import java.util.List;
1921
import java.util.function.Supplier;
2022

23+
import org.dataloader.DataLoaderRegistry;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
2127
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory;
2228
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContext;
23-
2429
import graphql.ExecutionInput;
2530
import graphql.GraphQL;
2631
import graphql.GraphQLContext;
32+
import graphql.execution.instrumentation.ChainedInstrumentation;
2733
import graphql.execution.instrumentation.Instrumentation;
34+
import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation;
35+
import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentationOptions;
2836
import graphql.schema.GraphQLCodeRegistry;
2937
import graphql.schema.GraphQLSchema;
3038
import graphql.schema.visibility.GraphqlFieldVisibility;
3139

3240
public class GraphQLJpaExecutorContext implements GraphQLExecutorContext {
33-
41+
42+
private final static Logger logger = LoggerFactory.getLogger(GraphQLJpaExecutorContext.class);
43+
3444
private final GraphQLSchema graphQLSchema;
3545
private final GraphQLExecutionInputFactory executionInputFactory;
3646
private final Supplier<GraphqlFieldVisibility> graphqlFieldVisibility;
3747
private final Supplier<Instrumentation> instrumentation;
3848
private final Supplier<GraphQLContext> graphqlContext;
49+
private final Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions;
50+
private final Supplier<DataLoaderRegistry> dataLoaderRegistry;
3951

4052
private GraphQLJpaExecutorContext(Builder builder) {
4153
this.graphQLSchema = builder.graphQLSchema;
4254
this.executionInputFactory = builder.executionInputFactory;
4355
this.graphqlFieldVisibility = builder.graphqlFieldVisibility;
4456
this.instrumentation = builder.instrumentation;
4557
this.graphqlContext = builder.graphqlContext;
58+
this.dataLoaderDispatcherInstrumentationOptions = builder.dataLoaderDispatcherInstrumentationOptions;
59+
this.dataLoaderRegistry = builder.dataLoaderRegistry;
4660
}
47-
61+
4862
@Override
4963
public ExecutionInput.Builder newExecutionInput() {
64+
DataLoaderRegistry dataLoaderRegistry = newDataLoaderRegistry();
65+
66+
GraphQLContext context = graphqlContext.get();
67+
68+
context.put("dataLoaderRegistry", dataLoaderRegistry);
69+
5070
return executionInputFactory.create()
51-
.context(graphqlContext.get());
71+
.dataLoaderRegistry(dataLoaderRegistry)
72+
.context(context);
5273
}
5374

5475
@Override
5576
public GraphQL.Builder newGraphQL() {
77+
Instrumentation instrumentation = newIstrumentation();
78+
5679
return GraphQL.newGraphQL(getGraphQLSchema())
57-
.instrumentation(instrumentation.get());
80+
.instrumentation(instrumentation);
81+
}
82+
83+
public DataLoaderRegistry newDataLoaderRegistry() {
84+
return dataLoaderRegistry.get();
85+
}
86+
87+
public Instrumentation newIstrumentation() {
88+
DataLoaderDispatcherInstrumentationOptions options = dataLoaderDispatcherInstrumentationOptions.get();
89+
90+
if (logger.isDebugEnabled()) {
91+
options.includeStatistics(true);
92+
}
93+
94+
DataLoaderDispatcherInstrumentation dispatcherInstrumentation = new DataLoaderDispatcherInstrumentation(options);
95+
96+
List<Instrumentation> list = Arrays.asList(dispatcherInstrumentation,
97+
instrumentation.get());
98+
99+
return new ChainedInstrumentation(list);
100+
58101
}
59-
102+
60103
@Override
61104
public GraphQLSchema getGraphQLSchema() {
62105
GraphQLCodeRegistry codeRegistry = graphQLSchema.getCodeRegistry()
63106
.transform(builder -> builder.fieldVisibility(graphqlFieldVisibility.get()));
64-
107+
65108
return graphQLSchema.transform(builder -> builder.codeRegistry(codeRegistry));
66109
}
67-
110+
68111
/**
69112
* Creates builder to build {@link GraphQLJpaExecutorContext}.
70113
* @return created builder
@@ -85,9 +128,13 @@ public interface IBuildStage {
85128
public IBuildStage graphqlFieldVisibility(Supplier<GraphqlFieldVisibility> graphqlFieldVisibility);
86129

87130
public IBuildStage instrumentation(Supplier<Instrumentation> instrumentation);
88-
131+
89132
public IBuildStage graphqlContext(Supplier<GraphQLContext> graphqlContext);
90133

134+
public IBuildStage dataLoaderDispatcherInstrumentationOptions(Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions);
135+
136+
public IBuildStage dataLoaderRegistry(Supplier<DataLoaderRegistry> dataLoaderRegistry);
137+
91138
public GraphQLJpaExecutorContext build();
92139
}
93140

@@ -101,6 +148,8 @@ public static final class Builder implements IGraphQLSchemaStage, IBuildStage {
101148
private Supplier<GraphqlFieldVisibility> graphqlFieldVisibility;
102149
private Supplier<Instrumentation> instrumentation;
103150
private Supplier<GraphQLContext> graphqlContext;
151+
private Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions;
152+
private Supplier<DataLoaderRegistry> dataLoaderRegistry;
104153

105154
private Builder() {
106155
}
@@ -135,10 +184,23 @@ public IBuildStage graphqlContext(Supplier<GraphQLContext> graphqlContext) {
135184
return this;
136185
}
137186

187+
@Override
188+
public IBuildStage dataLoaderDispatcherInstrumentationOptions(Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions) {
189+
this.dataLoaderDispatcherInstrumentationOptions = dataLoaderDispatcherInstrumentationOptions;
190+
191+
return this;
192+
}
193+
194+
@Override
195+
public IBuildStage dataLoaderRegistry(Supplier<DataLoaderRegistry> dataLoaderRegistry) {
196+
this.dataLoaderRegistry = dataLoaderRegistry;
197+
198+
return this;
199+
}
200+
138201
@Override
139202
public GraphQLJpaExecutorContext build() {
140203
return new GraphQLJpaExecutorContext(this);
141204
}
142-
143205
}
144206
}

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContextFactory.java

+39-9
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,46 @@
1818

1919
import java.util.function.Supplier;
2020

21+
import org.dataloader.DataLoaderOptions;
22+
import org.dataloader.DataLoaderRegistry;
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
2126
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory;
2227
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContext;
2328
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContextFactory;
24-
2529
import graphql.GraphQLContext;
2630
import graphql.execution.instrumentation.Instrumentation;
2731
import graphql.execution.instrumentation.SimpleInstrumentation;
32+
import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentationOptions;
2833
import graphql.schema.GraphQLSchema;
2934
import graphql.schema.visibility.DefaultGraphqlFieldVisibility;
3035
import graphql.schema.visibility.GraphqlFieldVisibility;
3136

3237
public class GraphQLJpaExecutorContextFactory implements GraphQLExecutorContextFactory {
33-
38+
private final static Logger logger = LoggerFactory.getLogger(GraphQLJpaExecutorContext.class);
39+
3440
private GraphQLExecutionInputFactory executionInputFactory = new GraphQLExecutionInputFactory() {};
3541
private Supplier<GraphqlFieldVisibility> graphqlFieldVisibility = () -> DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY;
3642
private Supplier<Instrumentation> instrumentation = () -> new SimpleInstrumentation();
3743
private Supplier<GraphQLContext> graphqlContext = () -> GraphQLContext.newContext().build();
38-
44+
private Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions = () -> {
45+
return DataLoaderDispatcherInstrumentationOptions.newOptions();
46+
};
47+
48+
private Supplier<DataLoaderOptions> dataLoaderOptions = () -> DataLoaderOptions.newOptions();
49+
50+
private Supplier<DataLoaderRegistry> dataLoaderRegistry = () -> {
51+
DataLoaderOptions options = dataLoaderOptions.get()
52+
.setCachingEnabled(false);
53+
54+
return BatchLoaderRegistry.newDataLoaderRegistry(options);
55+
};
56+
57+
3958
public GraphQLJpaExecutorContextFactory() {
4059
}
41-
60+
4261
@Override
4362
public GraphQLExecutorContext newExecutorContext(GraphQLSchema graphQLSchema) {
4463
return GraphQLJpaExecutorContext.builder()
@@ -47,6 +66,8 @@ public GraphQLExecutorContext newExecutorContext(GraphQLSchema graphQLSchema) {
4766
.graphqlFieldVisibility(graphqlFieldVisibility)
4867
.instrumentation(instrumentation)
4968
.graphqlContext(graphqlContext)
69+
.dataLoaderDispatcherInstrumentationOptions(dataLoaderDispatcherInstrumentationOptions)
70+
.dataLoaderRegistry(dataLoaderRegistry)
5071
.build();
5172
}
5273

@@ -55,13 +76,13 @@ public GraphQLJpaExecutorContextFactory withGraphqlFieldVisibility(Supplier<Grap
5576
return this;
5677
}
5778

58-
79+
5980
public GraphQLJpaExecutorContextFactory withInstrumentation(Supplier<Instrumentation> instrumentation) {
6081
this.instrumentation = instrumentation;
6182
return this;
6283
}
6384

64-
85+
6586
public GraphQLJpaExecutorContextFactory withExecutionInputFactory(GraphQLExecutionInputFactory executionInputFactory) {
6687
this.executionInputFactory = executionInputFactory;
6788
return this;
@@ -71,11 +92,16 @@ public GraphQLJpaExecutorContextFactory withGraphqlContext(Supplier<GraphQLConte
7192
this.graphqlContext = graphqlContext;
7293
return this;
7394
};
74-
95+
96+
public GraphQLJpaExecutorContextFactory withDataLoaderDispatcherInstrumentationOptions(Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions) {
97+
this.dataLoaderDispatcherInstrumentationOptions = dataLoaderDispatcherInstrumentationOptions;
98+
return this;
99+
}
100+
75101
public GraphQLExecutionInputFactory getExecutionInputFactory() {
76102
return executionInputFactory;
77103
}
78-
104+
79105
public Supplier<GraphqlFieldVisibility> getGraphqlFieldVisibility() {
80106
return graphqlFieldVisibility;
81107
}
@@ -84,9 +110,13 @@ public Supplier<Instrumentation> getInstrumentation() {
84110
return instrumentation;
85111
}
86112

87-
113+
88114
public Supplier<GraphQLContext> getGraphqlContext() {
89115
return graphqlContext;
90116
}
91117

118+
public Supplier<DataLoaderDispatcherInstrumentationOptions> getDataLoaderDispatcherInstrumentationOptions() {
119+
return dataLoaderDispatcherInstrumentationOptions;
120+
}
121+
92122
}

0 commit comments

Comments
 (0)