Skip to content

Commit c19f828

Browse files
refactor: update core gql libs
1 parent ad5bb7a commit c19f828

File tree

8 files changed

+102
-64
lines changed

8 files changed

+102
-64
lines changed

hypertrace-core-graphql-context/src/main/java/org/hypertrace/core/graphql/context/DefaultGraphQlRequestContextBuilder.java

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package org.hypertrace.core.graphql.context;
22

33
import com.google.common.collect.Streams;
4-
import graphql.kickstart.servlet.context.DefaultGraphQLServletContext;
4+
import graphql.kickstart.execution.context.DefaultGraphQLContext;
55
import graphql.kickstart.servlet.context.DefaultGraphQLServletContextBuilder;
6-
import graphql.kickstart.servlet.context.GraphQLServletContext;
76
import graphql.schema.DataFetcher;
87
import java.util.Arrays;
98
import java.util.Map;
@@ -14,10 +13,8 @@
1413
import java.util.stream.Collectors;
1514
import javax.annotation.Nonnull;
1615
import javax.inject.Inject;
17-
import javax.security.auth.Subject;
1816
import javax.servlet.http.HttpServletRequest;
1917
import javax.servlet.http.HttpServletResponse;
20-
import org.dataloader.DataLoaderRegistry;
2118
import org.hypertrace.core.graphql.spi.config.GraphQlServiceConfig;
2219

2320
class DefaultGraphQlRequestContextBuilder extends DefaultGraphQLServletContextBuilder
@@ -40,32 +37,22 @@ class DefaultGraphQlRequestContextBuilder extends DefaultGraphQLServletContextBu
4037

4138
@Override
4239
public GraphQlRequestContext build(HttpServletRequest request, HttpServletResponse response) {
43-
return new DefaultGraphQlRequestContext(request, response);
40+
return new DefaultGraphQlRequestContext(request);
4441
}
4542

46-
private final class DefaultGraphQlRequestContext implements GraphQlRequestContext {
47-
private final GraphQLServletContext servletContext;
43+
private final class DefaultGraphQlRequestContext extends DefaultGraphQLContext
44+
implements GraphQlRequestContext {
4845
private final ContextualCachingKey cachingKey;
4946

5047
private final String requestId = UUID.randomUUID().toString();
48+
private final HttpServletRequest request;
5149

52-
private DefaultGraphQlRequestContext(HttpServletRequest request, HttpServletResponse response) {
53-
this.servletContext =
54-
DefaultGraphQLServletContext.createServletContext().with(request).with(response).build();
50+
private DefaultGraphQlRequestContext(HttpServletRequest request) {
51+
this.request = request;
5552
this.cachingKey =
5653
new DefaultContextualCacheKey(this, this.getTenantId().orElse(DEFAULT_CONTEXT_ID));
5754
}
5855

59-
@Override
60-
public Optional<Subject> getSubject() {
61-
return this.servletContext.getSubject();
62-
}
63-
64-
@Override
65-
public Optional<DataLoaderRegistry> getDataLoaderRegistry() {
66-
return this.servletContext.getDataLoaderRegistry();
67-
}
68-
6956
@Override
7057
public <T> DataFetcher<CompletableFuture<T>> constructDataFetcher(
7158
Class<? extends DataFetcher<CompletableFuture<T>>> dataFetcherClass) {
@@ -75,29 +62,24 @@ public <T> DataFetcher<CompletableFuture<T>> constructDataFetcher(
7562

7663
@Override
7764
public Optional<String> getAuthorizationHeader() {
78-
HttpServletRequest request = this.servletContext.getHttpServletRequest();
7965
return Optional.ofNullable(request.getHeader(AUTHORIZATION_HEADER_KEY))
8066
.or(() -> Optional.ofNullable(request.getHeader(AUTHORIZATION_HEADER_KEY.toLowerCase())));
8167
}
8268

8369
@Override
8470
public Optional<String> getTenantId() {
85-
HttpServletRequest request = this.servletContext.getHttpServletRequest();
8671
return Optional.ofNullable(request.getHeader(TENANT_ID_HEADER_KEY))
8772
.or(DefaultGraphQlRequestContextBuilder.this.serviceConfig::getDefaultTenantId);
8873
}
8974

9075
@Override
9176
public Map<String, String> getTracingContextHeaders() {
92-
return Streams.stream(
93-
this.servletContext.getHttpServletRequest().getHeaderNames().asIterator())
77+
return Streams.stream(request.getHeaderNames().asIterator())
9478
.filter(
9579
header ->
9680
TRACING_CONTEXT_HEADER_KEY_PREFIXES.stream()
9781
.anyMatch(prefix -> header.toLowerCase().startsWith(prefix.toLowerCase())))
98-
.collect(
99-
Collectors.toUnmodifiableMap(
100-
String::toLowerCase, this.servletContext.getHttpServletRequest()::getHeader));
82+
.collect(Collectors.toUnmodifiableMap(String::toLowerCase, request::getHeader));
10183
}
10284

10385
@Nonnull

hypertrace-core-graphql-context/src/main/java/org/hypertrace/core/graphql/context/GraphQlRequestContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package org.hypertrace.core.graphql.context;
22

3-
import graphql.kickstart.execution.context.GraphQLContext;
3+
import graphql.kickstart.execution.context.GraphQLKickstartContext;
44
import graphql.schema.DataFetcher;
55
import java.util.Map;
66
import java.util.Optional;
77
import java.util.concurrent.CompletableFuture;
88
import javax.annotation.Nonnull;
99

10-
public interface GraphQlRequestContext extends GraphQLContext {
10+
public interface GraphQlRequestContext extends GraphQLKickstartContext {
1111

1212
/**
1313
* A tool to create data fetchers via injection container due to limitations in the framework. For

hypertrace-core-graphql-context/src/test/java/org/hypertrace/core/graphql/context/DefaultGraphQlRequestContextBuilderTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import static org.junit.jupiter.api.Assertions.assertNotEquals;
88
import static org.junit.jupiter.api.Assertions.assertNotNull;
99
import static org.junit.jupiter.api.Assertions.assertNotSame;
10-
import static org.junit.jupiter.api.Assertions.assertTrue;
1110
import static org.mockito.ArgumentMatchers.any;
1211
import static org.mockito.ArgumentMatchers.eq;
1312
import static org.mockito.Mockito.reset;
@@ -67,7 +66,7 @@ void returnsEmptyOptionalIfNoAuthorizationHeaderPresent() {
6766

6867
@Test
6968
void delegatesDataLoaderRegistry() {
70-
assertTrue(this.requestContext.getDataLoaderRegistry().isPresent());
69+
assertNotNull(this.requestContext.getDataLoaderRegistry());
7170
}
7271

7372
@Test

hypertrace-core-graphql-platform/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ dependencies {
1818
api("org.hypertrace.core.attribute.service:caching-attribute-service-client:0.14.8")
1919

2020
api("com.google.inject:guice:5.1.0")
21-
api("com.graphql-java:graphql-java:15.0")
22-
api("io.github.graphql-java:graphql-java-annotations:8.3")
21+
api("com.graphql-java:graphql-java:19.2")
22+
api("io.github.graphql-java:graphql-java-annotations:9.1")
2323
api("org.slf4j:slf4j-api:1.7.36")
2424
api("io.reactivex.rxjava3:rxjava:3.1.5")
2525
api("com.google.protobuf:protobuf-java-util:3.21.1")
@@ -28,7 +28,7 @@ dependencies {
2828
api("com.google.code.findbugs:jsr305:3.0.2")
2929
api("com.typesafe:config:1.4.2")
3030
api("com.google.guava:guava:31.1-jre")
31-
api("com.graphql-java-kickstart:graphql-java-servlet:10.1.0")
31+
api("com.graphql-java-kickstart:graphql-java-servlet:14.0.0")
3232

3333
api("com.fasterxml.jackson.core:jackson-databind:2.13.4")
3434
api("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4")

hypertrace-core-graphql-schema-registry/src/main/java/org/hypertrace/core/graphql/schema/registry/DefaultGraphQlSchemaRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public Set<GraphQlSchemaFragment> getRegisteredFragments() {
2222
}
2323

2424
@Override
25-
public GraphQlSchemaFragment getRootFragment() {
25+
public DefaultSchema getRootFragment() {
2626
return this.defaultSchemaFragment;
2727
}
2828
}

hypertrace-core-graphql-schema-registry/src/main/java/org/hypertrace/core/graphql/schema/registry/DefaultSchema.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
package org.hypertrace.core.graphql.schema.registry;
22

3+
import graphql.annotations.annotationTypes.GraphQLDescription;
4+
import graphql.annotations.annotationTypes.GraphQLField;
35
import graphql.annotations.annotationTypes.GraphQLName;
46
import org.hypertrace.core.graphql.spi.schema.GraphQlSchemaFragment;
57

68
class DefaultSchema implements GraphQlSchemaFragment {
9+
// Placeholder description is used to identify and remove placeholder fields before building the
10+
// schema. Placeholders are used while the schema is under construction so it is always valid
11+
// (i.e. has at least one value)
12+
static final String PLACEHOLDER_DESCRIPTION = "::placeholder::";
13+
static final String ROOT_QUERY_NAME = "Query";
14+
static final String ROOT_MUTATION_NAME = "Mutation";
715

816
@Override
917
public String fragmentName() {
@@ -20,9 +28,17 @@ public Class<?> annotatedMutationClass() {
2028
return MutationSchema.class;
2129
}
2230

23-
@GraphQLName("Query")
24-
private interface QuerySchema {}
31+
@GraphQLName(ROOT_QUERY_NAME)
32+
private interface QuerySchema {
33+
@GraphQLField
34+
@GraphQLDescription(PLACEHOLDER_DESCRIPTION)
35+
String placeholder();
36+
}
2537

26-
@GraphQLName("Mutation")
27-
private interface MutationSchema {}
38+
@GraphQLName(ROOT_MUTATION_NAME)
39+
private interface MutationSchema {
40+
@GraphQLField
41+
@GraphQLDescription(PLACEHOLDER_DESCRIPTION)
42+
String placeholder();
43+
}
2844
}

hypertrace-core-graphql-schema-registry/src/main/java/org/hypertrace/core/graphql/schema/registry/GraphQlAnnotatedSchemaMerger.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Collection;
1313
import java.util.Map;
1414
import java.util.Map.Entry;
15+
import java.util.Optional;
1516
import java.util.Set;
1617
import java.util.stream.Collectors;
1718
import javax.annotation.Nullable;
@@ -39,7 +40,8 @@ public GraphQLSchema get() {
3940

4041
return fragments.stream()
4142
.map(fragment -> this.fragmentToSchema(fragment, annotationProcessor))
42-
.reduce(this.fragmentToSchema(rootFragment, annotationProcessor), this::merge);
43+
.reduce(this.fragmentToSchema(rootFragment, annotationProcessor), this::merge)
44+
.transform(this::removeRootPlaceholders);
4345
}
4446

4547
private void registerAllTypeFunctions(
@@ -134,4 +136,36 @@ private Map<GraphQLObjectType, GraphQLObjectType> buildMapWithoutNulls(
134136
.filter(entry -> nonNull(entry) && nonNull(entry.getKey()) && nonNull(entry.getValue()))
135137
.collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue));
136138
}
139+
140+
private void removeRootPlaceholders(GraphQLSchema.Builder mergedSchema) {
141+
GraphQLSchema mergedWithPlaceholders = mergedSchema.build();
142+
// Queries must be defined, mutation is optional which is represented by a null
143+
mergedSchema.query(
144+
this.rebuildTypeWithoutPlaceholders(mergedWithPlaceholders.getQueryType()).orElseThrow());
145+
mergedSchema.mutation(
146+
this.rebuildTypeWithoutPlaceholders(mergedWithPlaceholders.getMutationType()).orElse(null));
147+
}
148+
149+
private Optional<GraphQLObjectType> rebuildTypeWithoutPlaceholders(GraphQLObjectType objectType) {
150+
// Schema validation now requires all objects to have at least one field. To merge partial
151+
// fragments, we start with placeholders, identified by a constant, then remove them at the end.
152+
GraphQLObjectType newType =
153+
GraphQLObjectType.newObject(objectType)
154+
.replaceFields(
155+
objectType.getFields().stream()
156+
.filter(
157+
field ->
158+
Optional.ofNullable(field.getDescription())
159+
.map(
160+
description ->
161+
!description.equals(DefaultSchema.PLACEHOLDER_DESCRIPTION))
162+
.orElse(true))
163+
.collect(Collectors.toList()))
164+
.build();
165+
166+
if (newType.getFields().isEmpty()) {
167+
return Optional.empty();
168+
}
169+
return Optional.of(newType);
170+
}
137171
}

hypertrace-core-graphql-schema-registry/src/test/java/org/hypertrace/core/graphql/schema/registry/GraphQlAnnotatedSchemaMergerTest.java

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package org.hypertrace.core.graphql.schema.registry;
22

3+
import static org.hypertrace.core.graphql.schema.registry.DefaultSchema.ROOT_MUTATION_NAME;
34
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertNull;
46
import static org.junit.jupiter.api.Assertions.assertTrue;
57
import static org.mockito.Mockito.when;
68

9+
import graphql.Scalars;
710
import graphql.annotations.annotationTypes.GraphQLDataFetcher;
811
import graphql.annotations.annotationTypes.GraphQLField;
9-
import graphql.annotations.annotationTypes.GraphQLName;
1012
import graphql.annotations.processor.ProcessingElementsContainer;
1113
import graphql.annotations.processor.typeFunctions.TypeFunction;
1214
import graphql.schema.DataFetcher;
@@ -30,20 +32,11 @@
3032
@ExtendWith(MockitoExtension.class)
3133
public class GraphQlAnnotatedSchemaMergerTest {
3234

33-
private static final String ROOT_QUERY_NAME = "root";
34-
private static final String ROOT_MUTATION_NAME = "rootMutation";
35-
3635
@Mock private GraphQlSchemaRegistry mockRegistry;
3736
@Mock private DataFetchingEnvironment mockDataFetchingEnvironment;
3837

3938
private GraphQlAnnotatedSchemaMerger merger;
4039

41-
@GraphQLName(ROOT_QUERY_NAME)
42-
interface RootQuerySchema {}
43-
44-
@GraphQLName(ROOT_MUTATION_NAME)
45-
interface RootMutationSchema {}
46-
4740
interface FirstQuerySchema {
4841
@GraphQLField
4942
String first();
@@ -91,8 +84,7 @@ interface SharedType {
9184
public void beforeEach() {
9285
this.merger = new GraphQlAnnotatedSchemaMerger(mockRegistry);
9386

94-
when(mockRegistry.getRootFragment())
95-
.thenReturn(this.createSchemaFragment(RootQuerySchema.class, RootMutationSchema.class));
87+
when(mockRegistry.getRootFragment()).thenReturn(new DefaultSchema());
9688
}
9789

9890
@Test
@@ -148,10 +140,13 @@ void mergesMutationSchemas() {
148140
@Test
149141
void supportsMutationOnlyFragment() {
150142
when(mockRegistry.getRegisteredFragments())
151-
.thenReturn(Set.of(this.createSchemaFragment(null, FirstMutationSchema.class)));
143+
.thenReturn(
144+
Set.of(
145+
this.createSchemaFragment(FirstQuerySchema.class),
146+
this.createSchemaFragment(null, FirstMutationSchema.class)));
152147

153148
this.verifySchemaWithMutationFields(this.merger.get(), Set.of("mutateOne"));
154-
this.verifySchemaWithQueryFields(this.merger.get(), Set.of());
149+
this.verifySchemaWithQueryFields(this.merger.get(), Set.of("first", "second"));
155150
}
156151

157152
@Test
@@ -169,7 +164,13 @@ public GraphQLType buildType(
169164
Class<?> aClass,
170165
AnnotatedType annotatedType,
171166
ProcessingElementsContainer container) {
172-
return GraphQLObjectType.newObject().name("typeFunctionType").build();
167+
return GraphQLObjectType.newObject()
168+
.name("typeFunctionType")
169+
.field(
170+
GraphQLFieldDefinition.newFieldDefinition()
171+
.name("typeFunctionTypeField")
172+
.type(Scalars.GraphQLString))
173+
.build();
173174
}
174175
};
175176

@@ -220,7 +221,7 @@ public List<TypeFunction> typeFunctions() {
220221
}
221222

222223
private void verifySchemaWithQueryFields(GraphQLSchema schema, Set<String> fields) {
223-
assertEquals(ROOT_QUERY_NAME, schema.getQueryType().getName());
224+
assertEquals(DefaultSchema.ROOT_QUERY_NAME, schema.getQueryType().getName());
224225
List<GraphQLFieldDefinition> fieldDefinitions = schema.getQueryType().getFieldDefinitions();
225226
assertEquals(fields.size(), fieldDefinitions.size());
226227
assertTrue(
@@ -231,13 +232,19 @@ private void verifySchemaWithQueryFields(GraphQLSchema schema, Set<String> field
231232
}
232233

233234
private void verifySchemaWithMutationFields(GraphQLSchema schema, Set<String> fields) {
234-
assertEquals(ROOT_MUTATION_NAME, schema.getMutationType().getName());
235-
List<GraphQLFieldDefinition> fieldDefinitions = schema.getMutationType().getFieldDefinitions();
236-
assertEquals(fields.size(), fieldDefinitions.size());
237-
assertTrue(
238-
fields.containsAll(
239-
fieldDefinitions.stream()
240-
.map(GraphQLFieldDefinition::getName)
241-
.collect(Collectors.toUnmodifiableSet())));
235+
if (fields.isEmpty()) {
236+
// A type must have fields, so if no fields expected, no type expected
237+
assertNull(schema.getMutationType());
238+
} else {
239+
assertEquals(ROOT_MUTATION_NAME, schema.getMutationType().getName());
240+
List<GraphQLFieldDefinition> fieldDefinitions =
241+
schema.getMutationType().getFieldDefinitions();
242+
assertEquals(fields.size(), fieldDefinitions.size());
243+
assertTrue(
244+
fields.containsAll(
245+
fieldDefinitions.stream()
246+
.map(GraphQLFieldDefinition::getName)
247+
.collect(Collectors.toUnmodifiableSet())));
248+
}
242249
}
243250
}

0 commit comments

Comments
 (0)