Skip to content

Commit

Permalink
Support of Subscriptions
Browse files Browse the repository at this point in the history
Upgrade of dependencies
  • Loading branch information
mishagr committed Nov 5, 2023
1 parent c9ab7d0 commit 386b8ad
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 76 deletions.
19 changes: 15 additions & 4 deletions demoapp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<parent>
<groupId>org.rapid-graphql</groupId>
<artifactId>rapid-graphql</artifactId>
<version>0.0.3</version>
<version>0.0.4</version>
</parent>

<artifactId>rapid-graphql-demoapp</artifactId>
<version>0.0.3</version>
<version>0.0.4</version>
<packaging>jar</packaging>

<name>demoapp</name>
Expand All @@ -27,10 +27,22 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.rapid-graphql</groupId>
<artifactId>rapid-graphql-starter</artifactId>
<version>0.0.3</version>
<version>0.0.4</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -70,7 +82,6 @@
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.rapidgraphql.helloworld;

import graphql.kickstart.tools.GraphQLSubscriptionResolver;
import graphql.schema.DataFetchingEnvironment;
import lombok.extern.log4j.Log4j2;
import org.reactivestreams.Publisher;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.time.Duration;

@Service
@Log4j2
class MySubscription implements GraphQLSubscriptionResolver {

public Publisher<Integer> hello(DataFetchingEnvironment env) {
return Flux.range(0, 100)
.delayElements(Duration.ofSeconds(1))
.map(this::fun);
}
private Integer fun(Integer i) {
Integer result = i*10;
log.info("result={}", result);
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RapidGraphQLApplicationTests {

@Test
Expand Down
12 changes: 6 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<version>2.7.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.rapid-graphql</groupId>
<artifactId>rapid-graphql</artifactId>
<version>0.0.3</version>
<version>0.0.4</version>
<name>rapid-graphql</name>
<description>Demo graphql booster app</description>
<modules>
Expand All @@ -21,11 +21,11 @@

<properties>
<java.version>11</java.version>
<graphql-spring-boot-starter.version>12.0.0</graphql-spring-boot-starter.version>
<graphql-java.version>17.3</graphql-java.version>
<graphql-spring-boot-starter.version>14.0.0</graphql-spring-boot-starter.version>
<graphql-java.version>21.0</graphql-java.version>
<checker-qual.version>3.21.2</checker-qual.version>
<graphql-scalars.version>17.0</graphql-scalars.version>
<spring-boot.version>2.6.3</spring-boot.version>
<graphql-scalars.version>17.1</graphql-scalars.version>
<spring-boot.version>2.7.15</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
Expand Down
11 changes: 7 additions & 4 deletions starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<parent>
<groupId>org.rapid-graphql</groupId>
<artifactId>rapid-graphql</artifactId>
<version>0.0.3</version>
<version>0.0.4</version>
</parent>

<artifactId>rapid-graphql-starter</artifactId>
<version>0.0.3</version>
<version>0.0.4</version>
<packaging>jar</packaging>

<name>starter</name>
Expand Down Expand Up @@ -41,7 +41,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down Expand Up @@ -71,7 +74,7 @@
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-extended-scalars</artifactId>
<version>17.0</version>
<version>${graphql-scalars.version}</version>
</dependency>
<!-- Parameter names -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,26 @@
package org.rapidgraphql.schemabuilder;

import graphql.VisibleForTesting;
import graphql.kickstart.tools.GraphQLMutationResolver;
import graphql.kickstart.tools.GraphQLQueryResolver;
import graphql.kickstart.tools.GraphQLResolver;
import graphql.kickstart.tools.SchemaError;
import graphql.language.Definition;
import graphql.language.DirectiveDefinition;
import graphql.language.DirectiveLocation;
import graphql.language.EnumTypeDefinition;
import graphql.language.EnumValueDefinition;
import graphql.language.FieldDefinition;
import graphql.language.InputObjectTypeDefinition;
import graphql.language.InputValueDefinition;
import graphql.language.ListType;
import graphql.language.NonNullType;
import graphql.language.ObjectTypeDefinition;
import graphql.language.ObjectTypeExtensionDefinition;
import graphql.kickstart.tools.*;
import graphql.language.Type;
import graphql.language.TypeName;
import graphql.language.*;
import graphql.scalars.ExtendedScalars;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLScalarType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import org.rapidgraphql.annotations.GraphQLIgnore;
import org.rapidgraphql.annotations.GraphQLInputType;
import org.rapidgraphql.directives.SecuredDirectiveWiring;
import org.slf4j.Logger;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Function;
Expand All @@ -60,18 +30,15 @@
import static java.lang.String.format;
import static java.util.Map.entry;
import static org.rapidgraphql.schemabuilder.MethodsFilter.*;
import static org.rapidgraphql.schemabuilder.TypeUtils.actualTypeArgument;
import static org.rapidgraphql.schemabuilder.TypeUtils.baseType;
import static org.rapidgraphql.schemabuilder.TypeUtils.castToParameterizedType;
import static org.rapidgraphql.schemabuilder.TypeUtils.isListType;
import static org.rapidgraphql.schemabuilder.TypeUtils.isNotNullable;
import static org.rapidgraphql.schemabuilder.TypeUtils.*;
import static org.slf4j.LoggerFactory.getLogger;

public class DefinitionFactory {

private static final Logger LOGGER = getLogger(DefinitionFactory.class);
public static final String QUERY_TYPE = "Query";
public static final String MUTATION_TYPE = "Mutation";
public static final String SUBSCRIPTION_TYPE = "Subscription";
private static final List<GraphQLScalarType> scalars = List.of(
ExtendedScalars.GraphQLLong,
ExtendedScalars.Date,
Expand Down Expand Up @@ -132,10 +99,14 @@ public Definition<?> createTypeDefinition(GraphQLResolver<?> resolver) {
String name;
Class<?> sourceType = null;
Class<?> resolverType = ClassUtils.getUserClass(resolver);
boolean isSubscription = false;
if (resolver instanceof GraphQLQueryResolver) {
name = QUERY_TYPE;
} else if(resolver instanceof GraphQLMutationResolver) {
name = MUTATION_TYPE;
} else if(resolver instanceof GraphQLSubscriptionResolver) {
name = SUBSCRIPTION_TYPE;
isSubscription = true;
} else {
Optional<DiscoveredClass> discoveredClass = extractResolverType(resolver);
if (discoveredClass.isEmpty()) {
Expand All @@ -148,8 +119,9 @@ public Definition<?> createTypeDefinition(GraphQLResolver<?> resolver) {
LOGGER.info("Processing {} resolver: {}", name, resolverType.getName());

final Class<?> finalSourceType = sourceType;
boolean finalIsSubscription = isSubscription;
Method[] resolverDeclaredMethods = ReflectionUtils.getUniqueDeclaredMethods(resolverType,
method -> resolverMethodFilter(finalSourceType, method));
method -> resolverMethodFilter(finalSourceType, method, finalIsSubscription));
List<FieldDefinition> typeFields = Arrays.stream(resolverDeclaredMethods)
.map(method -> createFieldDefinition(method, skipFirstParameter))
.collect(Collectors.toList());
Expand Down Expand Up @@ -348,7 +320,10 @@ private Type<?> convertToInputGraphQLType(AnnotatedType annotatedType) {
private Type<?> convertToGraphQLType(AnnotatedType annotatedType, TypeKind typeKind) {
Optional<AnnotatedParameterizedType> parameterizedType = castToParameterizedType(annotatedType);
Type<?> graphqlType;
if (parameterizedType.isPresent() && isListType(parameterizedType.get())) {
if (typeKind == TypeKind.OUTPUT_TYPE && parameterizedType.isPresent() && isPublisherType(parameterizedType.get())) {
AnnotatedType typeOfParameter = actualTypeArgument(parameterizedType.get(), 0);
graphqlType = convertToGraphQLType(typeOfParameter, typeKind);
} else if (parameterizedType.isPresent() && isListType(parameterizedType.get())) {
AnnotatedType typeOfParameter = actualTypeArgument(parameterizedType.get(), 0);
graphqlType = new ListType(convertToGraphQLType(typeOfParameter, typeKind));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ private List<Definition<?>> processResolvers(List<? extends GraphQLResolver<?>>
definitions.addAll(definitionFactory.getScalars().stream()
.map(scalar -> ScalarTypeDefinition.newScalarTypeDefinition().name(scalar.getName()).build())
.collect(Collectors.toList()));
definitions.addAll(resolvers.stream().map(resolver -> definitionFactory.createTypeDefinition(resolver)).collect(Collectors.toList()));
definitions.addAll(resolvers.stream()
.map(definitionFactory::createTypeDefinition)
.collect(Collectors.toList()));
definitions.addAll(definitionFactory.processTypesQueue());
return definitions;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.regex.Pattern;

import static java.lang.Character.isUpperCase;
import static org.rapidgraphql.schemabuilder.TypeUtils.isPublisherType;
import static org.slf4j.LoggerFactory.getLogger;

public class MethodsFilter {
Expand Down Expand Up @@ -52,7 +53,7 @@ private static boolean dataLoaderMethodFilter(Method method) {
return method.isAnnotationPresent(DataLoaderMethod.class);
}

public static boolean resolverMethodFilter(Class<?> sourceType, Method method) {
public static boolean resolverMethodFilter(Class<?> sourceType, Method method, boolean isSubscription) {
if (!typeMethodFilter(method)) {
return false;
}
Expand All @@ -61,6 +62,14 @@ public static boolean resolverMethodFilter(Class<?> sourceType, Method method) {
method.getDeclaringClass().getName(), method.getName(), sourceType.getName());
return false;
}
if (isSubscription) {
if ( !isPublisherType(method.getReturnType()) ) {
LOGGER.warn("Skipping method {}::{} in subscription resolver because it doesn't return publisher type",
method.getDeclaringClass().getName(), method.getName());
return false;
}
}

return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
package org.rapidgraphql.schemabuilder;

import graphql.kickstart.execution.context.DefaultGraphQLContext;
import graphql.kickstart.execution.context.GraphQLContext;
import graphql.kickstart.servlet.context.DefaultGraphQLServletContext;
import graphql.kickstart.servlet.context.DefaultGraphQLWebSocketContext;
import graphql.kickstart.execution.context.DefaultGraphQLContextBuilder;
import graphql.kickstart.execution.context.GraphQLKickstartContext;
import graphql.kickstart.servlet.context.GraphQLServletContextBuilder;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
//import jakarta.websocket.Session;
//import jakarta.websocket.server.HandshakeRequest;
import org.dataloader.DataLoaderRegistry;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;

public class RapidGraphQLContextBuilder implements GraphQLServletContextBuilder {
import java.util.HashMap;
import java.util.Map;

public class RapidGraphQLContextBuilder extends DefaultGraphQLContextBuilder
implements GraphQLServletContextBuilder {
private final DataLoaderRegistryFactory dataLoaderRegistryFactory;

public RapidGraphQLContextBuilder(DataLoaderRegistryFactory dataLoaderRegistryFactory) {
this.dataLoaderRegistryFactory = dataLoaderRegistryFactory;
}

@Override
public GraphQLContext build(HttpServletRequest req, HttpServletResponse response) {
return DefaultGraphQLServletContext.createServletContext(buildDataLoaderRegistry(), null).with(req).with(response)
.build();
}

@Override
public GraphQLContext build() {
return new DefaultGraphQLContext(buildDataLoaderRegistry(), null);
public GraphQLKickstartContext build(HttpServletRequest request, HttpServletResponse response) {
Map<Object, Object> map = new HashMap<>();
map.put(HttpServletRequest.class, request);
map.put(HttpServletResponse.class, response);
return GraphQLKickstartContext.of(buildDataLoaderRegistry(), map);
}

@Override
public GraphQLContext build(Session session, HandshakeRequest request) {
return DefaultGraphQLWebSocketContext.createWebSocketContext(buildDataLoaderRegistry(), null).with(session)
.with(request).build();
public GraphQLKickstartContext build(Session session, HandshakeRequest handshakeRequest) {
Map<Object, Object> map = new HashMap<>();
map.put(Session.class, session);
map.put(HandshakeRequest.class, handshakeRequest);
return GraphQLKickstartContext.of(buildDataLoaderRegistry(), map);
}

private DataLoaderRegistry buildDataLoaderRegistry() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.reactivestreams.Publisher;

import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
Expand Down Expand Up @@ -60,7 +61,16 @@ public static boolean isListType(Class<?> type) {
return List.class.isAssignableFrom(type);
}

public static Class<?> baseType(AnnotatedParameterizedType annotatedParameterizedType) {
public static boolean isPublisherType(AnnotatedParameterizedType type) {
Class<?> clazz = baseType(type);
return clazz.getTypeParameters().length==1 && Publisher.class.isAssignableFrom(clazz);
}

public static boolean isPublisherType(Class<?> type) {
return Publisher.class.isAssignableFrom(type);
}

public static Class<?> baseType(AnnotatedParameterizedType annotatedParameterizedType) {
Type rawType = ((ParameterizedType) annotatedParameterizedType.getType()).getRawType();
if (!(rawType instanceof Class<?>)) {
throw new RuntimeException("Parameterized type " + rawType.getTypeName() + " can't be processed");
Expand Down

0 comments on commit 386b8ad

Please sign in to comment.