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 26, 2024
1 parent da84bcc commit e21b5bf
Show file tree
Hide file tree
Showing 79 changed files with 1,684 additions and 602 deletions.
4 changes: 2 additions & 2 deletions .github/project.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: SmallRye GraphQL
release:
current-version: 2.9.2
next-version: 2.9.3-SNAPSHOT
current-version: 2.10.0
next-version: 2.10.1-SNAPSHOT
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ jobs:
- name: checkout Quarkus repository
uses: actions/checkout@v2
with:
repository: quarkusio/quarkus
ref: main
repository: jmartisk/quarkus
ref: smallrye-graphql-2.11
path: quarkus

- uses: actions/setup-java@v4
Expand Down
2 changes: 1 addition & 1 deletion client/api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-parent</artifactId>
<version>2.9.3-SNAPSHOT</version>
<version>2.10.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-graphql-client-api</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion client/generator-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-parent</artifactId>
<version>2.9.3-SNAPSHOT</version>
<version>2.10.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-graphql-client-generator-test</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion client/generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-parent</artifactId>
<version>2.9.3-SNAPSHOT</version>
<version>2.10.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-graphql-client-generator</artifactId>
Expand Down
41 changes: 35 additions & 6 deletions client/implementation-vertx/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-parent</artifactId>
<version>2.9.3-SNAPSHOT</version>
<version>2.10.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-graphql-client-implementation-vertx</artifactId>
Expand Down Expand Up @@ -49,6 +49,11 @@
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>io.smallrye</groupId>
Expand All @@ -65,10 +70,34 @@
<artifactId>smallrye-graphql-client-model-builder</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<!--This configuration separates the execution of the `TypesafeTckClientModelSuite` tests
from the default test execution (TypesafeTckSuite) to avoid conflicts or issues that might
arise when executing the same tests multiple times in different test suites.-->
<id>default-test</id>
<configuration>
<excludes>TypesafeTckClientModelSuite</excludes>
</configuration>
</execution>
<execution>
<id>separate-execution-for-client-model-tests</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<test>TypesafeTckClientModelSuite</test>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import java.util.List;
import java.util.Map;

import org.eclipse.microprofile.graphql.Name;
import org.jboss.logging.Logger;

import io.smallrye.graphql.api.Namespace;
import io.smallrye.graphql.client.impl.ErrorMessageProvider;
import io.smallrye.graphql.client.impl.GraphQLClientConfiguration;
import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration;
Expand Down Expand Up @@ -145,6 +147,14 @@ public VertxTypesafeGraphQLClientBuilder websocketInitializationTimeout(Integer

@Override
public <T> T build(Class<T> apiClass) {
Name nameAnnotation = apiClass.getAnnotation(Name.class);
Namespace namespaceAnnotation = apiClass.getAnnotation(Namespace.class);

if (nameAnnotation != null && namespaceAnnotation != null) {
throw new RuntimeException("You can only use one of the annotations - @Name or @Namespace " +
"over the GraphQLClientApi interface. Please, fix the following interface: " + apiClass.getName());
}

if (this.options == null) {
this.options = new WebClientOptions();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ private JsonObject request(MethodInvocation method) {
}
request.add("query", query);
request.add("variables", variables(method));
request.add("operationName", method.getName());
request.add("operationName", method.getOperationName());
JsonObject result = request.build();
log.tracef("full graphql request: %s", result.toString());
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ class TypesafeTckClientModelSuite extends TypesafeTCK {
static void beforeAll() {
// These properties are for the ‘TypesafeGraphQLClientBuilder#builder’ to differentiate between the typesafe-client
// using the client model and the one that does not.

System.clearProperty("clientModelCase");
System.setProperty("clientModelCase", "true");
}
Expand Down
2 changes: 1 addition & 1 deletion client/implementation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-parent</artifactId>
<version>2.9.3-SNAPSHOT</version>
<version>2.10.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-graphql-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,11 @@ public QueryBuilder(MethodInvocation method) {
public String build() {
StringBuilder request = new StringBuilder(method.getOperationTypeAsString());
request.append(" ");
request.append(method.getName());
request.append(method.getOperationName());
if (method.hasValueParameters())
request.append(method.valueParameters().map(this::declare).collect(joining(", ", "(", ")")));

String groupName = method.getGroupName();
if (groupName != null) {
request.append(" { ");
request.append(groupName);
}
method.getNamespaces().forEach(namespace -> request.append(" { ").append(namespace));

if (method.isSingle()) {
request.append(" { ");
Expand All @@ -45,11 +41,11 @@ public String build() {

request.append(fields(method.getReturnType()));

if (method.isSingle())
if (method.isSingle()) {
request.append(" }");
}

if (groupName != null)
request.append(" } ");
request.append(" }".repeat(method.getNamespaces().size()));

return request.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,12 @@ public Object read() {
private JsonObject readData() {
if (!response.containsKey("data") || response.isNull("data"))
return null;
String groupName = method.getGroupName();
JsonObject data = groupName != null
? response.getJsonObject("data").getJsonObject(groupName)
: response.getJsonObject("data");

JsonObject data = response.getJsonObject("data");
for (String namespace : method.getNamespaces()) {
data = data.getJsonObject(namespace);
}

if (method.isSingle() && !data.containsKey(method.getName()))
throw new InvalidResponseException("No data for '" + method.getName() + "'");
return data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

Expand All @@ -23,6 +24,7 @@
import org.eclipse.microprofile.graphql.Name;
import org.eclipse.microprofile.graphql.Query;

import io.smallrye.graphql.api.Namespace;
import io.smallrye.graphql.api.Subscription;
import io.smallrye.graphql.client.core.OperationType;
import io.smallrye.graphql.client.model.MethodKey;
Expand All @@ -36,14 +38,16 @@ public static MethodInvocation of(Method method, Object... args) {
private final TypeInfo type;
private final Method method;
private final Object[] parameterValues;
private final String groupName;
private final List<String> namespaces;
private final String operationName;
private List<ParameterInfo> parameters;

private MethodInvocation(TypeInfo type, Method method, Object[] parameterValues) {
this.type = type;
this.method = method;
this.parameterValues = parameterValues;
this.groupName = readGroupName(method);
this.namespaces = readNamespaces(method);
this.operationName = readOperationName(this.namespaces);
}

@Override
Expand Down Expand Up @@ -262,18 +266,41 @@ public String getOperationTypeAsString() {
}
}

public String getGroupName() {
return groupName;
public List<String> getNamespaces() {
return namespaces;
}

private String readGroupName(Method method) {
Name annotation = method.getDeclaringClass().getAnnotation(Name.class);
if (annotation != null) {
String groupName = annotation.value().trim();
if (!groupName.isEmpty()) {
return groupName;
public String getOperationName() {
return operationName;
}

private List<String> readNamespaces(Method method) {
if (method.getDeclaringClass().isAnnotationPresent(Namespace.class)) {
String[] names = method.getDeclaringClass().getAnnotation(Namespace.class).value();
if (names.length > 0) {
return List.of(names);
}
} else if (method.getDeclaringClass().isAnnotationPresent(Name.class)) {
String name = method.getDeclaringClass().getAnnotation(Name.class).value();
if (!name.isBlank()) {
return List.of(name);
}
}
return List.of();
}

private String readOperationName(List<String> names) {
if (names.isEmpty()) {
return getName();
} else {
String namespace = names.stream()
.map(this::makeFirstLetterUppercase)
.collect(Collectors.joining());
return namespace + makeFirstLetterUppercase(getName());
}
return null;
}

private String makeFirstLetterUppercase(String value) {
return value.substring(0, 1).toUpperCase() + value.substring(1);
}
}
2 changes: 1 addition & 1 deletion client/model-builder/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-parent</artifactId>
<version>2.9.3-SNAPSHOT</version>
<version>2.10.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-graphql-client-model-builder</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ private static Map<DotName, AnnotationInstance> getAnnotationsWithFilter(Type ty
public static final DotName NON_BLOCKING = DotName.createSimple("io.smallrye.common.annotation.NonBlocking");

// SmallRye GraphQL Annotations (Experimental)
public static final DotName NAMESPACE = DotName.createSimple("io.smallrye.graphql.api.Namespace");
public static final DotName TO_SCALAR = DotName.createSimple("io.smallrye.graphql.api.ToScalar"); // TODO: Remove
public static final DotName ADAPT_TO_SCALAR = DotName.createSimple("io.smallrye.graphql.api.AdaptToScalar");
public static final DotName ADAPT_WITH = DotName.createSimple("io.smallrye.graphql.api.AdaptWith");
Expand Down Expand Up @@ -578,6 +579,7 @@ private static Map<DotName, AnnotationInstance> getAnnotationsWithFilter(Type ty
public static final DotName JAKARTA_JSONB_TRANSIENT = DotName.createSimple(JAKARTA_JSONB + "JsonbTransient");
public static final DotName JAKARTA_JSONB_CREATOR = DotName.createSimple(JAKARTA_JSONB + "JsonbCreator");
public static final DotName JAKARTA_JSONB_TYPE_ADAPTER = DotName.createSimple(JAKARTA_JSONB + "JsonbTypeAdapter");
public static final DotName JAKARTA_JSONB_TYPE_INFO = DotName.createSimple(JAKARTA_JSONB + "JsonbTypeInfo");

// Jackson Annotations
public static final DotName JACKSON_IGNORE = DotName.createSimple("com.fasterxml.jackson.annotation.JsonIgnore");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ public static boolean isAsync(Type type) {
|| isMulti(type);
}

public static boolean isInterface(Type type) {
if (Classes.isClass(type)) {
ClassInfo clazz = getIndex().getClassByName(type.asClassType().name());
return clazz != null && clazz.isInterface();
}
return false;
}

public static boolean isUni(Type type) {
return type.name().equals(UNI);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package io.smallrye.graphql.client.model;

import static io.smallrye.graphql.client.model.Annotations.GRAPHQL_CLIENT_API;
import static io.smallrye.graphql.client.model.Annotations.NAME;
import static io.smallrye.graphql.client.model.Annotations.NAMESPACE;
import static io.smallrye.graphql.client.model.ScanningContext.getIndex;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
Expand Down Expand Up @@ -56,6 +59,8 @@ private ClientModels generateClientModels() {
Collection<AnnotationInstance> graphQLApiAnnotations = getIndex()
.getAnnotations(GRAPHQL_CLIENT_API);

validateNamespaceAnnotations(graphQLApiAnnotations);

graphQLApiAnnotations.forEach(graphQLApiAnnotation -> {
ClientModel operationMap = new ClientModel();
ClassInfo apiClass = graphQLApiAnnotation.target().asClass();
Expand All @@ -75,6 +80,19 @@ private ClientModels generateClientModels() {
return clientModels;
}

private void validateNamespaceAnnotations(Collection<AnnotationInstance> graphQLApiAnnotations) {
List<String> errorInterfaces = graphQLApiAnnotations.stream()
.map(annotation -> annotation.target().asClass())
.filter(classInfo -> classInfo.hasDeclaredAnnotation(NAMESPACE) && classInfo.hasDeclaredAnnotation(NAME))
.map(classInfo -> classInfo.name().toString())
.collect(Collectors.toList());
if (!errorInterfaces.isEmpty()) {
throw new RuntimeException("You can only use one of the annotations - @Name or @Namespace " +
"over the GraphQLClientApi interface. Please, fix the following interfaces: " +
String.join(", ", errorInterfaces));
}
}

/**
* Retrieves all methods, including those from superclasses (interfaces).
*
Expand Down
Loading

0 comments on commit e21b5bf

Please sign in to comment.