Skip to content

Commit 84c58dd

Browse files
CNAChinoashpak-shaikhkmoore-intuitAureus, Carlo
authored
Federation mvp (#48)
* DOS-1266: intial federation key support * Allowing transformer to see federation interface entites * Change the service type * Change resource set builder to concatenate files to read defaults only once and rename service type to federation subgraph * Federation entity collection * federation shared types support * add method for isFederatedProvider * rename entities variable to entitiesByTypeName * entity-type-merging: initial commit * federation metadata classes * Support for FieldSet scalar * intital federation require support * make exceptions more generic to apply to more directives and fix shared type edge case * KeyTransformPostmerge integration and adding test * Verify keys defined in extension are a subset of the base entity * collecting key directives in FederationMetadata * removing entity batching * enrich entity's selection set if keys are not selected * use base entity keys for representation * resolve required fields during normal field resolution instead as an entity fetch request * ensure extension fields are not in base, and refactor exceptions to be in package * Federation support for type extension * DOS-1386: merging of value type support * validate fetches entity response * Add new constant for response and refactor to use constants * Add validation for entity fetcher Co-authored-by: Ashpak Shaikh <[email protected]> Co-authored-by: kmoore-intuit <[email protected]> Co-authored-by: Aureus, Carlo <[email protected]> Co-authored-by: kmoore15 <[email protected]> Co-authored-by: Shaikh <[email protected]>
1 parent b3bffb2 commit 84c58dd

File tree

80 files changed

+4878
-262
lines changed

Some content is hidden

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

80 files changed

+4878
-262
lines changed

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>com.intuit.graphql</groupId>
44
<artifactId>graphql-orchestrator-java</artifactId>
5-
<version>4.1.8-SNAPSHOT</version>
5+
<version>5.0.0-SNAPSHOT</version>
66
<packaging>jar</packaging>
77
<name>graphql-orchestrator-java</name>
88
<description>GraphQL Orchestrator combines multiple graphql services into a single unified schema</description>
@@ -37,7 +37,7 @@
3737
<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
3838
<awssdk2.version>2.7.2</awssdk2.version>
3939
<graphql-sdl-version>3.0.1</graphql-sdl-version>
40-
<xtextVersion>2.25.0</xtextVersion>
40+
<xtextVersion>2.26.0</xtextVersion>
4141
<graphQLVersion>17.3</graphQLVersion>
4242
</properties>
4343

@@ -377,7 +377,7 @@
377377
<connection>scm:git:https://github.com/intuit/graphql-orchestrator-java
378378
</connection>
379379
<url>https://github.com/intuit/graphql-orchestrator-java</url>
380-
<tag>graphql-orchestrator-java-4.0.12</tag>
380+
<tag>graphql-orchestrator-java-5.0.0-beta3</tag>
381381
</scm>
382382

383383
<distributionManagement>
Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import static graphql.util.TreeTransformerUtil.deleteNode;
88
import static java.util.Objects.requireNonNull;
99

10+
import com.intuit.graphql.orchestrator.federation.RequiredFieldsCollector;
11+
import com.intuit.graphql.orchestrator.federation.metadata.FederationMetadata;
1012
import com.intuit.graphql.orchestrator.schema.ServiceMetadata;
1113
import com.intuit.graphql.orchestrator.schema.transform.FieldResolverContext;
1214
import com.intuit.graphql.orchestrator.utils.SelectionCollector;
@@ -24,23 +26,37 @@
2426
import graphql.schema.GraphQLTypeUtil;
2527
import graphql.util.TraversalControl;
2628
import graphql.util.TraverserContext;
27-
import java.util.HashSet;
29+
import java.util.Collections;
2830
import java.util.List;
2931
import java.util.Map;
3032
import java.util.Objects;
3133
import java.util.Set;
3234
import java.util.stream.Collectors;
3335
import org.apache.commons.collections4.CollectionUtils;
34-
35-
public class NoExternalReferenceSelectionSetModifier extends NodeVisitorStub {
36+
import org.apache.commons.collections4.MapUtils;
37+
38+
/**
39+
* This class modifies for query for a downstream provider.
40+
*
41+
* One function of this class is to remove external fields. This occurs if a type
42+
* is extended by other services and add fields to it.
43+
*
44+
* Another function is this class adds required fields to the query if fields
45+
* are required by other sibling fields which are external or remote.
46+
*/
47+
public class DownstreamQueryModifier extends NodeVisitorStub {
3648

3749
private final GraphQLFieldsContainer rootType;
3850
private final ServiceMetadata serviceMetadata;
3951
private final SelectionCollector selectionCollector;
4052

41-
NoExternalReferenceSelectionSetModifier(GraphQLFieldsContainer rootType,
53+
DownstreamQueryModifier(
54+
GraphQLFieldsContainer rootType,
4255
ServiceMetadata serviceMetadata,
4356
Map<String, FragmentDefinition> fragmentsByName) {
57+
Objects.requireNonNull(rootType);
58+
Objects.requireNonNull(serviceMetadata);
59+
Objects.requireNonNull(fragmentsByName);
4460
this.rootType = rootType;
4561
this.serviceMetadata = serviceMetadata;
4662
this.selectionCollector = new SelectionCollector(fragmentsByName);
@@ -57,7 +73,12 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {
5773
String fieldName = node.getName();
5874
GraphQLFieldDefinition fieldDefinition = getFieldDefinition(fieldName, parentType);
5975
requireNonNull(fieldDefinition, "Failed to get Field Definition for " + fieldName);
60-
if (hasResolverDirective(fieldDefinition)) {
76+
77+
// TODO consider the entire condition to be abstracted in
78+
// serviceMetadata.isFieldExternal(fieldCoordinates).
79+
// This requires a complete set of field coordinates that the service owns
80+
if (hasResolverDirective(fieldDefinition)
81+
|| isExternalField(parentType.getName(), fieldName)) {
6182
return deleteNode(context);
6283
}
6384

@@ -69,15 +90,22 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {
6990
}
7091
}
7192

72-
private GraphQLFieldDefinition getFieldDefinition(String name, GraphQLFieldsContainer parentType) {
93+
private boolean isExternalField(String parentTypename, String fieldName) {
94+
FieldCoordinates fieldCoordinates = coordinates(parentTypename, fieldName);
95+
return serviceMetadata.isOwnedByEntityExtension(fieldCoordinates);
96+
}
97+
98+
private GraphQLFieldDefinition getFieldDefinition(
99+
String name, GraphQLFieldsContainer parentType) {
73100
if (TypeNameMetaFieldDef.getName().equals(name)) {
74101
return TypeNameMetaFieldDef;
75102
}
76103
return parentType.getFieldDefinition(name);
77104
}
78105

79106
@Override
80-
public TraversalControl visitFragmentDefinition(FragmentDefinition node, TraverserContext<Node> context) {
107+
public TraversalControl visitFragmentDefinition(
108+
FragmentDefinition node, TraverserContext<Node> context) {
81109
// if modifying selection set in a fragment definition, this will be the first code to visit.
82110
context.setVar(GraphQLType.class, rootType);
83111
return TraversalControl.CONTINUE;
@@ -102,19 +130,18 @@ public TraversalControl visitSelectionSet(SelectionSet node, TraverserContext<No
102130
context.setVar(GraphQLType.class, parentType);
103131
String parentTypeName = parentType.getName();
104132

105-
Map<String, Field> selectedFields = this.selectionCollector.collectFields(node);
106-
Set<Field> fieldsToAdd = new HashSet<>();
107-
getFieldsWithResolverDirective(parentTypeName, selectedFields)
108-
.forEach(fieldResolverContext -> {
109-
Set<String> fldResolverReqdFields = fieldResolverContext.getRequiredFields();
110-
if (CollectionUtils.isNotEmpty(fldResolverReqdFields)) {
111-
fieldsToAdd.addAll(
112-
fldResolverReqdFields.stream()
113-
.filter(s -> !selectedFields.containsKey(s))
114-
.map(s -> Field.newField(s).build())
115-
.collect(Collectors.toSet()));
116-
}
117-
});
133+
Map<String, Field> selectedFields = this.selectionCollector.collectFields(node);
134+
135+
RequiredFieldsCollector fedRequiredFieldsCollector = RequiredFieldsCollector
136+
.builder()
137+
.excludeFields(selectedFields)
138+
.parentTypeName(parentTypeName)
139+
.serviceMetadata(this.serviceMetadata)
140+
.fieldResolverContexts(getFieldsWithResolverDirective(parentTypeName, selectedFields))
141+
.fieldsWithRequiresDirective(getFieldsWithRequiresDirective(parentTypeName, selectedFields))
142+
.build();
143+
144+
Set<Field> fieldsToAdd = fedRequiredFieldsCollector.get();
118145

119146
if (CollectionUtils.isNotEmpty(fieldsToAdd)) {
120147
SelectionSet newNode = node.transform(builder -> {
@@ -129,7 +156,6 @@ public TraversalControl visitSelectionSet(SelectionSet node, TraverserContext<No
129156
return TraversalControl.CONTINUE;
130157
}
131158

132-
133159
private List<FieldResolverContext> getFieldsWithResolverDirective(
134160
String parentTypename, Map<String, Field> selectedFields) {
135161
return selectedFields.values().stream()
@@ -142,9 +168,23 @@ private List<FieldResolverContext> getFieldsWithResolverDirective(
142168
.collect(Collectors.toList());
143169
}
144170

145-
private GraphQLType getParentType(@SuppressWarnings("rawtypes") TraverserContext<Node> context) {
171+
private Set<Field> getFieldsWithRequiresDirective(String parentTypename, Map<String, Field> selectedFields) {
172+
if (MapUtils.isEmpty(selectedFields)) {
173+
return Collections.emptySet();
174+
}
175+
176+
FederationMetadata federationMetadata = this.serviceMetadata.getFederationServiceMetadata();
177+
return selectedFields.values().stream()
178+
.filter(field -> isExternalField(parentTypename, field.getName()))
179+
.filter(field -> {
180+
FieldCoordinates fieldCoordinates = coordinates(parentTypename, field.getName());
181+
return federationMetadata.hasRequiresFieldSet(fieldCoordinates);
182+
})
183+
.collect(Collectors.toSet());
184+
}
185+
186+
private GraphQLType getParentType(TraverserContext<Node> context) {
146187
GraphQLType parentType = context.getParentContext().getVar(GraphQLType.class);
147188
return GraphQLTypeUtil.unwrapAll(parentType);
148189
}
149-
150-
}
190+
}

src/main/java/com/intuit/graphql/orchestrator/batch/FieldResolverBatchLoader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private OperationDefinition removeExternalFields(OperationDefinition operationDe
116116
GraphQLObjectType rootType = graphQLSchema.getQueryType();
117117
Map<String, FragmentDefinition> fragmentsByName = dataFetchingEnvironment.getFragmentsByName();
118118
return (OperationDefinition) AST_TRANSFORMER.transform(operationDefinition,
119-
new NoExternalReferenceSelectionSetModifier((GraphQLFieldsContainer) unwrapAll(rootType), serviceMetadata, fragmentsByName));
119+
new DownstreamQueryModifier((GraphQLFieldsContainer) unwrapAll(rootType), serviceMetadata, fragmentsByName));
120120
}
121121

122122
private ServiceMetadata getServiceMetadata(DataFetchingEnvironment dataFetchingEnvironment) {

src/main/java/com/intuit/graphql/orchestrator/batch/GraphQLServiceBatchLoader.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,9 @@ private CompletableFuture<List<DataFetcherResult<Object>>> load(List<DataFetchin
116116
MergedField filteredRootField = result.getMergedField();
117117
if (filteredRootField != null) {
118118
filteredRootField.getFields().stream()
119-
.map(field -> serviceMetadata.hasFieldResolverDirective()
120-
? removeFieldsWithExternalTypes(field, getRootFieldDefinition(key.getExecutionStepInfo()).getType(), key.getFragmentsByName())
121-
: field
122-
)
119+
.map(field -> (serviceMetadata.shouldRemoveExternalFields())
120+
? removeFieldsWithExternalTypes(field, getRootFieldDefinition(key.getExecutionStepInfo()).getType(), key.getFragmentsByName())
121+
: field)
123122
.forEach(selectionSetBuilder::selection);
124123
}
125124

@@ -142,7 +141,7 @@ private CompletableFuture<List<DataFetcherResult<Object>>> load(List<DataFetchin
142141

143142
Map<String, FragmentDefinition> svcFragmentDefinitions = filterFragmentDefinitionByService(
144143
key.getFragmentsByName());
145-
if (serviceMetadata.hasFieldResolverDirective()) {
144+
if (serviceMetadata.hasFieldResolverDirective() || serviceMetadata.isFederationService()) {
146145
Map<String, FragmentDefinition> finalServiceFragmentDefinitions = new HashMap<>();
147146
svcFragmentDefinitions.forEach((fragmentName, fragmentDefinition) -> {
148147
String typeConditionName = fragmentDefinition.getTypeCondition().getName();
@@ -266,15 +265,15 @@ private CompletableFuture<Map<String, Object>> execute(GraphQLContext context, O
266265
*
267266
* @param origFragmentDefinition original fragment definition
268267
* @param typeCondition type condition of the original fragment definition. This will be used as
269-
* the root type for {@link NoExternalReferenceSelectionSetModifier}
268+
* the root type for {@link DownstreamQueryModifier}
270269
* @return a modified fragment definition
271270
*/
272271
private FragmentDefinition removeFieldsWithExternalTypes(final FragmentDefinition origFragmentDefinition,
273272
GraphQLType typeCondition, Map<String, FragmentDefinition> fragmentsByName) {
274273
// call serviceMetadata.hasFieldResolverDirective() before calling this method
275274
return (FragmentDefinition) AST_TRANSFORMER.transform(origFragmentDefinition,
276-
new NoExternalReferenceSelectionSetModifier((GraphQLFieldsContainer) unwrapAll(typeCondition),
277-
serviceMetadata, fragmentsByName));
275+
new DownstreamQueryModifier((GraphQLFieldsContainer) unwrapAll(typeCondition), serviceMetadata, fragmentsByName));
276+
278277
}
279278

280279
/**
@@ -301,14 +300,13 @@ private FragmentDefinition removeDomainTypeFromFragment(final FragmentDefinition
301300
*
302301
* @param origField field to be processed.
303302
* @param fieldType the type of origField. This will be used as the root type for {@link
304-
* NoExternalReferenceSelectionSetModifier}
303+
* DownstreamQueryModifier}
305304
* @return a modified field
306305
*/
307-
private Field removeFieldsWithExternalTypes(Field origField, GraphQLOutputType fieldType, Map<String,
308-
FragmentDefinition> fragmentsByName) {
306+
private Field removeFieldsWithExternalTypes(Field origField, GraphQLOutputType fieldType, Map<String, FragmentDefinition> fragmentsByName) {
309307
// call serviceMetadata.hasFieldResolverDirective() before calling this method
310308
return (Field) AST_TRANSFORMER.transform(origField,
311-
new NoExternalReferenceSelectionSetModifier((GraphQLFieldsContainer) unwrapAll(fieldType), serviceMetadata, fragmentsByName));
309+
new DownstreamQueryModifier((GraphQLFieldsContainer) unwrapAll(fieldType), serviceMetadata, fragmentsByName));
312310
}
313311

314312
private GraphQLSchema getSchema(List<DataFetchingEnvironment> environments) {

0 commit comments

Comments
 (0)