Skip to content

Commit

Permalink
Added resolver for federation queries
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Lovakov committed Sep 4, 2024
1 parent da84bcc commit a87737b
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ private static Map<DotName, AnnotationInstance> getAnnotationsWithFilter(org.jbo
public static final DotName ERROR_CODE = DotName.createSimple("io.smallrye.graphql.api.ErrorCode");
public static final DotName DATAFETCHER = DotName.createSimple("io.smallrye.graphql.api.DataFetcher");
public static final DotName SUBCRIPTION = DotName.createSimple("io.smallrye.graphql.api.Subscription");
public static final DotName RESOLVER = DotName.createSimple("io.smallrye.graphql.api.federation.Resolver");
public static final DotName DIRECTIVE = DotName.createSimple("io.smallrye.graphql.api.Directive");
public static final DotName DEFAULT_NON_NULL = DotName.createSimple("io.smallrye.graphql.api.DefaultNonNull");
public static final DotName NULLABLE = DotName.createSimple("io.smallrye.graphql.api.Nullable");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ private void addOperations(Optional<Group> group, Schema schema, List<MethodInfo
} else {
schema.addSubscription(subscription);
}
} else if (annotationsForMethod.containsOneOfTheseAnnotations(Annotations.RESOLVER)) {
Operation resolver = operationCreator.createOperation(methodInfo, OperationType.RESOLVER, null);
schema.addResolver(resolver);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ private static DotName getOperationAnnotation(OperationType operationType) {
return Annotations.MUTATION;
case SUBSCRIPTION:
return Annotations.SUBCRIPTION;
case RESOLVER:
return Annotations.RESOLVER;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
public enum OperationType {
QUERY,
MUTATION,
SUBSCRIPTION
SUBSCRIPTION,
RESOLVER,
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public final class Schema implements Serializable {
private Set<Operation> queries = new HashSet<>();
private Set<Operation> mutations = new HashSet<>();
private Set<Operation> subscriptions = new HashSet<>();
private Set<Operation> resolvers = new HashSet<>();

private Map<Group, Set<Operation>> groupedQueries = new HashMap<>();
private Map<Group, Set<Operation>> groupedMutations = new HashMap<>();
Expand Down Expand Up @@ -95,6 +96,22 @@ public boolean hasSubscriptions() {
return !this.subscriptions.isEmpty();
}

public Set<Operation> getResolvers() {
return resolvers;
}

public void setResolvers(Set<Operation> resolvers) {
this.resolvers = resolvers;
}

public void addResolver(Operation resolver) {
this.resolvers.add(resolver);
}

public boolean hasResolvers() {
return !this.resolvers.isEmpty();
}

public Map<Group, Set<Operation>> getGroupedQueries() {
return groupedQueries;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.smallrye.graphql.api.federation;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import io.smallrye.common.annotation.Experimental;

@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Experimental("Resolver method without creating query method")
public @interface Resolver {
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import io.smallrye.graphql.api.federation.Override;
import io.smallrye.graphql.api.federation.Provides;
import io.smallrye.graphql.api.federation.Requires;
import io.smallrye.graphql.api.federation.Resolver;
import io.smallrye.graphql.api.federation.Shareable;
import io.smallrye.graphql.api.federation.Tag;
import io.smallrye.graphql.api.federation.link.Import;
Expand Down Expand Up @@ -126,6 +127,7 @@ private IndexView createCustomIndex() {
indexer.index(convertClassToInputStream(Shareable.class));
indexer.index(convertClassToInputStream(Tag.class));
indexer.index(convertClassToInputStream(OneOf.class));
indexer.index(convertClassToInputStream(Resolver.class));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import com.apollographql.federation.graphqljava.Federation;

import graphql.Scalars;
import graphql.introspection.Introspection.DirectiveLocation;
import graphql.schema.Coercing;
import graphql.schema.DataFetcher;
Expand Down Expand Up @@ -124,7 +125,7 @@ public static GraphQLSchema bootstrap(Schema schema) {
}

public static GraphQLSchema bootstrap(Schema schema, boolean skipInjectionValidation) {
if (schema != null && (schema.hasOperations())) {
if (schema != null && (schema.hasOperations()) || Config.get().isFederationEnabled()) {
Bootstrap bootstrap = new Bootstrap(schema, skipInjectionValidation);
bootstrap.generateGraphQLSchema();
return bootstrap.graphQLSchema;
Expand Down Expand Up @@ -185,7 +186,11 @@ private void generateGraphQLSchema() {
createGraphQLObjectTypes();
createGraphQLInputObjectTypes();

addQueries(schemaBuilder);
GraphQLObjectType resolversType = Config.get().isFederationEnabled()
? buildResolvers()
: GraphQLObjectType.newObject().name("Resolver").build();

GraphQLObjectType queryRootType = addQueries(schemaBuilder);
addMutations(schemaBuilder);
addSubscriptions(schemaBuilder);
schemaBuilder.withSchemaAppliedDirectives(Arrays.stream(
Expand Down Expand Up @@ -214,9 +219,20 @@ private void generateGraphQLSchema() {

if (Config.get().isFederationEnabled()) {
log.enableFederation();

if (!(schema.hasQueries() || schema.hasResolvers() || schema.hasMutations() || schema.hasSubscriptions())) {
throw new RuntimeException("You must define at least one method marked with one of the annotations - "
+ "@Query, @Mutation, @Subscription, @Resolver. An empty diagram has no meaning");
}

// hack! For schema build success if queries are empty.
// It will be overrides in Federation transformation
addSdlQueryToSchema(schemaBuilder, queryRootType);

GraphQLSchema rawSchema = schemaBuilder.build();
this.graphQLSchema = Federation.transform(rawSchema)
.fetchEntities(new FederationDataFetcher(rawSchema.getQueryType(), rawSchema.getCodeRegistry()))
.fetchEntities(
new FederationDataFetcher(resolversType, rawSchema.getQueryType(), rawSchema.getCodeRegistry()))
.resolveEntityType(fetchEntityType())
.setFederation2(true)
.build();
Expand All @@ -225,6 +241,35 @@ private void generateGraphQLSchema() {
}
}

private void addSdlQueryToSchema(GraphQLSchema.Builder schemaBuilder, GraphQLObjectType queryRootType) {
GraphQLObjectType type = GraphQLObjectType.newObject()
.name("_Service")
.field(GraphQLFieldDefinition
.newFieldDefinition().name("sdl")
.type(new GraphQLNonNull(Scalars.GraphQLString))
.build())
.build();

GraphQLFieldDefinition field = GraphQLFieldDefinition.newFieldDefinition()
.name("_service")
.type(GraphQLNonNull.nonNull(type))
.build();

GraphQLObjectType.Builder newQueryType = GraphQLObjectType.newObject(queryRootType);

newQueryType.field(field);
schemaBuilder.query(newQueryType.build());
}

private GraphQLObjectType buildResolvers() {
GraphQLObjectType.Builder queryBuilder = GraphQLObjectType.newObject()
.name("Resolver");
if (schema.hasResolvers()) {
addRootObject(queryBuilder, schema.getResolvers(), "Resolver");
}
return queryBuilder.build();
}

private TypeResolver fetchEntityType() {
return env -> {
Object src = env.getObject();
Expand Down Expand Up @@ -326,7 +371,7 @@ private void createGraphQLDirectiveType(DirectiveType directiveType) {
directiveTypes.add(directiveBuilder.build());
}

private void addQueries(GraphQLSchema.Builder schemaBuilder) {
private GraphQLObjectType addQueries(GraphQLSchema.Builder schemaBuilder) {
GraphQLObjectType.Builder queryBuilder = GraphQLObjectType.newObject()
.name(QUERY)
.description(QUERY_DESCRIPTION);
Expand All @@ -340,6 +385,7 @@ private void addQueries(GraphQLSchema.Builder schemaBuilder) {

GraphQLObjectType query = queryBuilder.build();
schemaBuilder.query(query);
return query;
}

private void addMutations(GraphQLSchema.Builder schemaBuilder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ public class FederationDataFetcher implements DataFetcher<CompletableFuture<List

public static final String TYPENAME = "__typename";
private final GraphQLObjectType queryType;
private final GraphQLObjectType resolversType;
private final GraphQLCodeRegistry codeRegistry;
private final HashMap<TypeAndArgumentNames, TypeFieldWrapper> cache = new HashMap<>();

public FederationDataFetcher(GraphQLObjectType queryType, GraphQLCodeRegistry codeRegistry) {
public FederationDataFetcher(GraphQLObjectType resolversType, GraphQLObjectType queryType,
GraphQLCodeRegistry codeRegistry) {
this.resolversType = resolversType;
this.queryType = queryType;
this.codeRegistry = codeRegistry;
}
Expand Down Expand Up @@ -115,6 +118,11 @@ private TypeFieldWrapper findBatchFieldDefinition(TypeAndArgumentNames typeAndAr
return typeFieldWrapper;
}
}
for (GraphQLFieldDefinition field : resolversType.getFields()) {
if (matchesReturnTypeList(field, typeAndArgumentNames.type) && matchesArguments(typeAndArgumentNames, field)) {
return new TypeFieldWrapper(resolversType, field);
}
}
return null;
}

Expand All @@ -131,7 +139,11 @@ private TypeFieldWrapper findFieldDefinition(TypeAndArgumentNames typeAndArgumen
return typeFieldWrapper;
}
}

for (GraphQLFieldDefinition field : resolversType.getFields()) {
if (matchesReturnType(field, typeAndArgumentNames.type) && matchesArguments(typeAndArgumentNames, field)) {
return new TypeFieldWrapper(resolversType, field);
}
}
throw new RuntimeException(
"no query found for " + typeAndArgumentNames.type + " by " + typeAndArgumentNames.argumentNames);
}
Expand Down

0 comments on commit a87737b

Please sign in to comment.