Skip to content

Commit ad26ee7

Browse files
authoredNov 27, 2023
Add support for transactional delegate execution strategy builder customizer (#406)
1 parent 9e7d6b6 commit ad26ee7

File tree

4 files changed

+160
-10
lines changed

4 files changed

+160
-10
lines changed
 

‎autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaBuilderAutoConfiguration.java

+21-6
Original file line numberDiff line numberDiff line change
@@ -46,36 +46,51 @@ GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate(PlatformTransa
4646
@ConditionalOnMissingBean(QueryExecutionStrategyProvider.class)
4747
@ConditionalOnSingleCandidate(GraphQLSchemaTransactionTemplate.class)
4848
QueryExecutionStrategyProvider queryExecutionStrategy(
49-
GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate
49+
GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate,
50+
ObjectProvider<TransactionalExecutionStrategyCustomizer<QueryExecutionStrategyProvider>> executionStrategyCustomizer
5051
) {
5152
var transactionTemplate = graphQLSchemaTransactionTemplate.get();
5253
transactionTemplate.setReadOnly(true);
5354

54-
return () -> newTransactionalExecutionStrategy(transactionTemplate).build();
55+
var executionStrategy = newTransactionalExecutionStrategy(transactionTemplate);
56+
57+
executionStrategyCustomizer.ifAvailable(customizer -> customizer.accept(executionStrategy));
58+
59+
return executionStrategy::build;
5560
}
5661

5762
@Bean
5863
@ConditionalOnMissingBean(MutationExecutionStrategyProvider.class)
5964
@ConditionalOnSingleCandidate(GraphQLSchemaTransactionTemplate.class)
6065
MutationExecutionStrategyProvider mutationExecutionStrategy(
61-
GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate
66+
GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate,
67+
ObjectProvider<TransactionalExecutionStrategyCustomizer<MutationExecutionStrategyProvider>> executionStrategyCustomizer
6268
) {
6369
var transactionTemplate = graphQLSchemaTransactionTemplate.get();
6470
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
6571

66-
return () -> newTransactionalExecutionStrategy(transactionTemplate).build();
72+
var executionStrategy = newTransactionalExecutionStrategy(transactionTemplate);
73+
74+
executionStrategyCustomizer.ifAvailable(customizer -> customizer.accept(executionStrategy));
75+
76+
return executionStrategy::build;
6777
}
6878

6979
@Bean
7080
@ConditionalOnMissingBean(SubscriptionExecutionStrategyProvider.class)
7181
@ConditionalOnSingleCandidate(GraphQLSchemaTransactionTemplate.class)
7282
SubscriptionExecutionStrategyProvider subscriptionExecutionStrategy(
73-
GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate
83+
GraphQLSchemaTransactionTemplate graphQLSchemaTransactionTemplate,
84+
ObjectProvider<TransactionalExecutionStrategyCustomizer<SubscriptionExecutionStrategyProvider>> executionStrategyCustomizer
7485
) {
7586
var transactionTemplate = graphQLSchemaTransactionTemplate.get();
7687
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
7788

78-
return () -> newTransactionalExecutionStrategy(transactionTemplate).build();
89+
var executionStrategy = newTransactionalExecutionStrategy(transactionTemplate);
90+
91+
executionStrategyCustomizer.ifAvailable(customizer -> customizer.accept(executionStrategy));
92+
93+
return executionStrategy::build;
7994
}
8095

8196
@Bean

‎autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/TransactionalDelegateExecutionStrategy.java

+25-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.concurrent.Executor;
1111
import java.util.concurrent.ExecutorService;
1212
import java.util.concurrent.Executors;
13+
import java.util.function.Consumer;
1314
import java.util.function.Supplier;
1415
import org.slf4j.Logger;
1516
import org.slf4j.LoggerFactory;
@@ -50,8 +51,8 @@ public CompletableFuture<ExecutionResult> execute(
5051
);
5152
}
5253
return CompletableFuture.supplyAsync(
53-
() -> {
54-
return transactionTemplate.execute(status -> {
54+
() ->
55+
transactionTemplate.execute(status -> {
5556
if (log.isTraceEnabled()) {
5657
log.trace(
5758
"Begin transaction for {} on {}",
@@ -78,8 +79,7 @@ public CompletableFuture<ExecutionResult> execute(
7879
);
7980
}
8081
}
81-
});
82-
},
82+
}),
8383
executor.get()
8484
);
8585
} else {
@@ -95,6 +95,18 @@ public CompletableFuture<ExecutionResult> execute(
9595
}
9696
}
9797

98+
public TransactionTemplate getTransactionTemplate() {
99+
return transactionTemplate;
100+
}
101+
102+
public ExecutionStrategy getDelegate() {
103+
return delegate;
104+
}
105+
106+
public Supplier<Executor> getExecutor() {
107+
return executor;
108+
}
109+
98110
public static final class Builder {
99111

100112
private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
@@ -118,6 +130,11 @@ public Builder transactionTemplate(TransactionTemplate transactionTemplate) {
118130
return this;
119131
}
120132

133+
public Builder transactionTemplate(Consumer<TransactionTemplate> transactionTemplateCustomizer) {
134+
transactionTemplateCustomizer.accept(transactionTemplate);
135+
return this;
136+
}
137+
121138
public Builder delegate(ExecutionStrategy delegate) {
122139
this.delegate = delegate;
123140
return this;
@@ -128,6 +145,10 @@ public Builder executor(Supplier<Executor> executor) {
128145
return this;
129146
}
130147

148+
public Builder executor(Executor executor) {
149+
return executor(() -> executor);
150+
}
151+
131152
public TransactionalDelegateExecutionStrategy build() {
132153
return new TransactionalDelegateExecutionStrategy(transactionTemplate, delegate, executor);
133154
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.introproventures.graphql.jpa.query.autoconfigure;
2+
3+
import graphql.execution.ExecutionStrategy;
4+
import java.util.function.Consumer;
5+
import java.util.function.Supplier;
6+
7+
public interface TransactionalExecutionStrategyCustomizer<T extends Supplier<ExecutionStrategy>>
8+
extends Consumer<TransactionalDelegateExecutionStrategy.Builder> {}

‎autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java

+106
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring;
1010
import static java.util.Collections.emptyMap;
1111
import static org.assertj.core.api.Assertions.assertThat;
12+
import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED;
1213

1314
import com.introproventures.graphql.jpa.query.autoconfigure.support.AdditionalGraphQLType;
1415
import com.introproventures.graphql.jpa.query.autoconfigure.support.MutationRoot;
@@ -26,6 +27,7 @@
2627
import graphql.annotations.annotationTypes.GraphQLInvokeDetached;
2728
import graphql.annotations.annotationTypes.GraphQLName;
2829
import graphql.annotations.annotationTypes.directives.definition.GraphQLDirectiveDefinition;
30+
import graphql.execution.AsyncSerialExecutionStrategy;
2931
import graphql.scalars.ExtendedScalars;
3032
import graphql.schema.DataFetcher;
3133
import graphql.schema.GraphQLCodeRegistry;
@@ -54,6 +56,7 @@
5456
import java.util.function.Supplier;
5557
import java.util.stream.Collectors;
5658
import java.util.stream.Stream;
59+
import org.assertj.core.api.InstanceOfAssertFactories;
5760
import org.junit.jupiter.api.Test;
5861
import org.reactivestreams.Publisher;
5962
import org.reflections.Reflections;
@@ -65,7 +68,10 @@
6568
import org.springframework.context.annotation.Bean;
6669
import org.springframework.context.annotation.Configuration;
6770
import org.springframework.core.io.Resource;
71+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
6872
import org.springframework.stereotype.Component;
73+
import org.springframework.transaction.TransactionDefinition;
74+
import org.springframework.transaction.support.DefaultTransactionDefinition;
6975
import org.springframework.util.StringUtils;
7076
import reactor.core.publisher.Flux;
7177
import reactor.test.StepVerifier;
@@ -106,6 +112,40 @@ GraphQLSchemaEntityManager persistentContextEntityManager() {
106112
return () -> entityManager;
107113
}
108114

115+
@Bean
116+
TransactionalExecutionStrategyCustomizer<QueryExecutionStrategyProvider> queryExecutionStrategyCustomizer(
117+
ThreadPoolTaskExecutor threadPoolTaskExecutor
118+
) {
119+
return builder ->
120+
builder
121+
.executor(threadPoolTaskExecutor)
122+
.delegate(new AsyncSerialExecutionStrategy())
123+
.transactionTemplate(transactionTemplate -> transactionTemplate.setTimeout(999));
124+
}
125+
126+
@Bean
127+
TransactionalExecutionStrategyCustomizer<MutationExecutionStrategyProvider> mutationExecutionStrategyCustomizer(
128+
ThreadPoolTaskExecutor threadPoolTaskExecutor
129+
) {
130+
return builder ->
131+
builder
132+
.executor(threadPoolTaskExecutor)
133+
.delegate(new AsyncSerialExecutionStrategy())
134+
.transactionTemplate(transactionTemplate -> transactionTemplate.setTimeout(999));
135+
}
136+
137+
@Bean
138+
TransactionalExecutionStrategyCustomizer<SubscriptionExecutionStrategyProvider> subscriptionExecutionStrategyCustomizer(
139+
ThreadPoolTaskExecutor threadPoolTaskExecutor
140+
) {
141+
return builder ->
142+
builder
143+
.executor(threadPoolTaskExecutor)
144+
.delegate(new AsyncSerialExecutionStrategy())
145+
.transactionTemplate(transactionTemplate -> transactionTemplate.setTimeout(999))
146+
.transactionTemplate(transactionTemplate -> transactionTemplate.setReadOnly(true));
147+
}
148+
109149
@Configuration
110150
static class GraphQLAnnotationsSchemaConfigurer implements GraphQLSchemaConfigurer {
111151

@@ -415,4 +455,70 @@ void configuresSharedEntityManager() {
415455
// given
416456
assertThat(graphQLJpaSchemaBuilder.getEntityManager()).isEqualTo(entityManagerSupplier.get());
417457
}
458+
459+
@Test
460+
void configuresQueryTransactionalExecutionStrategyCustomizer() {
461+
// given
462+
assertThat(queryExecutionStrategy)
463+
.isInstanceOf(QueryExecutionStrategyProvider.class)
464+
.extracting(Supplier::get)
465+
.asInstanceOf(InstanceOfAssertFactories.type(TransactionalDelegateExecutionStrategy.class))
466+
.satisfies(result -> {
467+
assertThat(result.getDelegate()).isInstanceOf(AsyncSerialExecutionStrategy.class);
468+
assertThat(result.getExecutor()).extracting(Supplier::get).isInstanceOf(ThreadPoolTaskExecutor.class);
469+
assertThat(result.getTransactionTemplate())
470+
.extracting(DefaultTransactionDefinition::getTimeout)
471+
.isEqualTo(999);
472+
assertThat(result.getTransactionTemplate())
473+
.extracting(DefaultTransactionDefinition::getPropagationBehavior)
474+
.isEqualTo(PROPAGATION_REQUIRED);
475+
assertThat(result.getTransactionTemplate())
476+
.extracting(DefaultTransactionDefinition::isReadOnly)
477+
.isEqualTo(true);
478+
});
479+
}
480+
481+
@Test
482+
void configuresMutationTransactionalExecutionStrategyCustomizer() {
483+
// given
484+
assertThat(mutationExecutionStrategy)
485+
.isInstanceOf(MutationExecutionStrategyProvider.class)
486+
.extracting(Supplier::get)
487+
.asInstanceOf(InstanceOfAssertFactories.type(TransactionalDelegateExecutionStrategy.class))
488+
.satisfies(result -> {
489+
assertThat(result.getDelegate()).isInstanceOf(AsyncSerialExecutionStrategy.class);
490+
assertThat(result.getExecutor()).extracting(Supplier::get).isInstanceOf(ThreadPoolTaskExecutor.class);
491+
assertThat(result.getTransactionTemplate())
492+
.extracting(DefaultTransactionDefinition::getTimeout)
493+
.isEqualTo(999);
494+
assertThat(result.getTransactionTemplate())
495+
.extracting(DefaultTransactionDefinition::getPropagationBehavior)
496+
.isEqualTo(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
497+
assertThat(result.getTransactionTemplate())
498+
.extracting(DefaultTransactionDefinition::isReadOnly)
499+
.isEqualTo(false);
500+
});
501+
}
502+
503+
@Test
504+
void configuresSubscriptionTransactionalExecutionStrategyCustomizer() {
505+
// given
506+
assertThat(subscriptionExecutionStrategy)
507+
.isInstanceOf(SubscriptionExecutionStrategyProvider.class)
508+
.extracting(Supplier::get)
509+
.asInstanceOf(InstanceOfAssertFactories.type(TransactionalDelegateExecutionStrategy.class))
510+
.satisfies(result -> {
511+
assertThat(result.getDelegate()).isInstanceOf(AsyncSerialExecutionStrategy.class);
512+
assertThat(result.getExecutor()).extracting(Supplier::get).isInstanceOf(ThreadPoolTaskExecutor.class);
513+
assertThat(result.getTransactionTemplate())
514+
.extracting(DefaultTransactionDefinition::getTimeout)
515+
.isEqualTo(999);
516+
assertThat(result.getTransactionTemplate())
517+
.extracting(DefaultTransactionDefinition::getPropagationBehavior)
518+
.isEqualTo(TransactionDefinition.PROPAGATION_SUPPORTS);
519+
assertThat(result.getTransactionTemplate())
520+
.extracting(DefaultTransactionDefinition::isReadOnly)
521+
.isEqualTo(true);
522+
});
523+
}
418524
}

0 commit comments

Comments
 (0)
Please sign in to comment.