Apply checkstyle changes to spring-graphql
See gh-943
This commit is contained in:
@@ -61,7 +61,7 @@ public interface GraphQlRequest {
|
||||
/**
|
||||
* Convert the request to a {@link Map} as defined in
|
||||
* <a href="https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md">GraphQL over HTTP</a> and
|
||||
* <a href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md">GraphQL over WebSocket</a>:
|
||||
* <a href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md">GraphQL over WebSocket</a>.
|
||||
* <table>
|
||||
* <tr><th>Key</th><th>Value</th></tr>
|
||||
* <tr><td>query</td><td>{@link #getDocument() document}</td></tr>
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,7 +43,7 @@ public interface ResponseField {
|
||||
* </ul>
|
||||
* @deprecated as of 1.0.3 in favor of checking via {@link #getValue()}
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since = "1.0.3", forRemoval = true)
|
||||
boolean hasValue();
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,7 @@ public interface ResponseField {
|
||||
* @deprecated since 1.0.3 in favor of {@link #getErrors()}
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
@Deprecated(since = "1.0.3", forRemoval = true)
|
||||
ResponseError getError();
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.util.ClassUtils;
|
||||
* agnostic {@code GraphQlClient}. A transport specific extension can then wrap
|
||||
* this default tester by extending {@link AbstractDelegatingGraphQlClient}.
|
||||
*
|
||||
* @param <B> the builder type
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
* @see AbstractDelegatingGraphQlClient
|
||||
@@ -114,6 +115,8 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
|
||||
* Transport-specific subclasses can provide their JSON {@code Encoder} and
|
||||
* {@code Decoder} for use at the client level, for mapping response data
|
||||
* to some target entity type.
|
||||
* @param encoder the JSON encoder to use
|
||||
* @param decoder the JSON decoder to use
|
||||
*/
|
||||
protected void setJsonCodecs(Encoder<?> encoder, Decoder<?> decoder) {
|
||||
this.jsonEncoder = encoder;
|
||||
@@ -122,6 +125,7 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
|
||||
|
||||
/**
|
||||
* Variant of {@link #setJsonCodecs} for setting each codec individually.
|
||||
* @param encoder the JSON encoder to use
|
||||
*/
|
||||
protected void setJsonEncoder(Encoder<?> encoder) {
|
||||
this.jsonEncoder = encoder;
|
||||
@@ -137,6 +141,7 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
|
||||
|
||||
/**
|
||||
* Variant of {@link #setJsonCodecs} for setting each codec individually.
|
||||
* @param decoder the JSON decoder to use
|
||||
*/
|
||||
protected void setJsonDecoder(Decoder<?> decoder) {
|
||||
this.jsonDecoder = decoder;
|
||||
@@ -161,12 +166,13 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
|
||||
/**
|
||||
* Build the default transport-agnostic client that subclasses can then wrap
|
||||
* with {@link AbstractDelegatingGraphQlClient}.
|
||||
* @param transport the GraphQL transport to be used by the client
|
||||
*/
|
||||
protected GraphQlClient buildGraphQlClient(GraphQlTransport transport) {
|
||||
|
||||
if (jackson2Present) {
|
||||
this.jsonEncoder = (this.jsonEncoder == null ? DefaultJackson2Codecs.encoder() : this.jsonEncoder);
|
||||
this.jsonDecoder = (this.jsonDecoder == null ? DefaultJackson2Codecs.decoder() : this.jsonDecoder);
|
||||
this.jsonEncoder = (this.jsonEncoder == null) ? DefaultJackson2Codecs.encoder() : this.jsonEncoder;
|
||||
this.jsonDecoder = (this.jsonDecoder == null) ? DefaultJackson2Codecs.decoder() : this.jsonDecoder;
|
||||
}
|
||||
|
||||
return new DefaultGraphQlClient(
|
||||
@@ -177,32 +183,32 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
|
||||
* Return a {@code Consumer} to initialize new builders from "this" builder.
|
||||
*/
|
||||
protected Consumer<AbstractGraphQlClientBuilder<?>> getBuilderInitializer() {
|
||||
return builder -> {
|
||||
builder.interceptors(interceptorList -> interceptorList.addAll(interceptors));
|
||||
builder.documentSource(documentSource);
|
||||
return (builder) -> {
|
||||
builder.interceptors((interceptorList) -> interceptorList.addAll(this.interceptors));
|
||||
builder.documentSource(this.documentSource);
|
||||
builder.setJsonCodecs(getEncoder(), getDecoder());
|
||||
};
|
||||
}
|
||||
|
||||
private Chain createExecuteChain(GraphQlTransport transport) {
|
||||
|
||||
Chain chain = request -> transport.execute(request).map(response ->
|
||||
Chain chain = (request) -> transport.execute(request).map((response) ->
|
||||
new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
|
||||
|
||||
return this.interceptors.stream()
|
||||
.reduce(GraphQlClientInterceptor::andThen)
|
||||
.map(interceptor -> (Chain) (request) -> interceptor.intercept(request, chain))
|
||||
.map((interceptor) -> (Chain) (request) -> interceptor.intercept(request, chain))
|
||||
.orElse(chain);
|
||||
}
|
||||
|
||||
private SubscriptionChain createExecuteSubscriptionChain(GraphQlTransport transport) {
|
||||
|
||||
SubscriptionChain chain = request -> transport.executeSubscription(request)
|
||||
.map(response -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
|
||||
SubscriptionChain chain = (request) -> transport.executeSubscription(request)
|
||||
.map((response) -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
|
||||
|
||||
return this.interceptors.stream()
|
||||
.reduce(GraphQlClientInterceptor::andThen)
|
||||
.map(interceptor -> (SubscriptionChain) (request) -> interceptor.interceptSubscription(request, chain))
|
||||
.map((interceptor) -> (SubscriptionChain) (request) -> interceptor.interceptSubscription(request, chain))
|
||||
.orElse(chain);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,12 @@ public interface ClientGraphQlResponse extends GraphQlResponse {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
ClientResponseField field(String path);
|
||||
|
||||
/**
|
||||
* Decode the full response map to the given target type.
|
||||
* @param <D> the target type
|
||||
* @param type the target class
|
||||
* @return the decoded value, or never {@code null}
|
||||
* @throws FieldAccessException if the response is not {@link #isValid() valid}
|
||||
@@ -44,7 +46,8 @@ public interface ClientGraphQlResponse extends GraphQlResponse {
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
* @param type the target type
|
||||
* @param <D> the target type
|
||||
* @param type the target parameterized type
|
||||
* @return the decoded value, or never {@code null}
|
||||
* @throws FieldAccessException if the response is not {@link #isValid() valid}
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ public interface ClientResponseField extends ResponseField {
|
||||
|
||||
/**
|
||||
* Decode the field to an entity of the given type.
|
||||
* @param <D> the entity type
|
||||
* @param entityType the type to convert to
|
||||
* @return the decoded entity, or {@code null} if the field is {@code null}
|
||||
* but otherwise there are no errors
|
||||
@@ -46,12 +47,15 @@ public interface ClientResponseField extends ResponseField {
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
* @param <D> the entity type
|
||||
* @param entityType the type to convert to
|
||||
*/
|
||||
@Nullable
|
||||
<D> D toEntity(ParameterizedTypeReference<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
|
||||
* @param <D> the entity type
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the list of decoded entities, or an empty list if the field is
|
||||
* {@code null} but otherwise there are no errors
|
||||
@@ -61,15 +65,16 @@ public interface ClientResponseField extends ResponseField {
|
||||
*/
|
||||
<D> List<D> toEntityList(Class<D> elementType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the list of decoded entities, or an empty list if the field is
|
||||
* {@code null} but otherwise there are no errors
|
||||
* @throws FieldAccessException if the target field is {@code null} and the
|
||||
* response is not {@link GraphQlResponse#isValid() valid} or the field has
|
||||
* {@link ResponseField#getErrors() errors}.
|
||||
*/
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
|
||||
* @param <D> the entity type
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the list of decoded entities, or an empty list if the field is
|
||||
* {@code null} but otherwise there are no errors
|
||||
* @throws FieldAccessException if the target field is {@code null} and the
|
||||
* response is not {@link GraphQlResponse#isValid() valid} or the field has
|
||||
* {@link ResponseField#getErrors() errors}.
|
||||
*/
|
||||
<D> List<D> toEntityList(ParameterizedTypeReference<D> elementType);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.client;
|
||||
|
||||
import java.util.List;
|
||||
@@ -37,7 +38,6 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
* Helper class for encoding and decoding GraphQL messages.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class CodecDelegate {
|
||||
|
||||
@@ -60,14 +60,14 @@ final class CodecDelegate {
|
||||
|
||||
static Encoder<?> findJsonEncoder(CodecConfigurer configurer) {
|
||||
return findJsonEncoder(configurer.getWriters().stream()
|
||||
.filter(writer -> writer instanceof EncoderHttpMessageWriter)
|
||||
.map(writer -> ((EncoderHttpMessageWriter<?>) writer).getEncoder()));
|
||||
.filter((writer) -> writer instanceof EncoderHttpMessageWriter)
|
||||
.map((writer) -> ((EncoderHttpMessageWriter<?>) writer).getEncoder()));
|
||||
}
|
||||
|
||||
static Decoder<?> findJsonDecoder(CodecConfigurer configurer) {
|
||||
return findJsonDecoder(configurer.getReaders().stream()
|
||||
.filter(reader -> reader instanceof DecoderHttpMessageReader)
|
||||
.map(reader -> ((DecoderHttpMessageReader<?>) reader).getDecoder()));
|
||||
.filter((reader) -> reader instanceof DecoderHttpMessageReader)
|
||||
.map((reader) -> ((DecoderHttpMessageReader<?>) reader).getDecoder()));
|
||||
}
|
||||
|
||||
static Encoder<?> findJsonEncoder(List<Encoder<?>> encoders) {
|
||||
@@ -80,26 +80,26 @@ final class CodecDelegate {
|
||||
|
||||
private static Encoder<?> findJsonEncoder(Stream<Encoder<?>> stream) {
|
||||
return stream
|
||||
.filter(encoder -> encoder.canEncode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
|
||||
.filter((encoder) -> encoder.canEncode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("No JSON Encoder"));
|
||||
}
|
||||
|
||||
private static Decoder<?> findJsonDecoder(Stream<Decoder<?>> decoderStream) {
|
||||
return decoderStream
|
||||
.filter(decoder -> decoder.canDecode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
|
||||
.filter((decoder) -> decoder.canDecode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("No JSON Decoder"));
|
||||
}
|
||||
|
||||
|
||||
public CodecConfigurer getCodecConfigurer() {
|
||||
CodecConfigurer getCodecConfigurer() {
|
||||
return this.codecConfigurer;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> WebSocketMessage encode(WebSocketSession session, GraphQlWebSocketMessage message) {
|
||||
<T> WebSocketMessage encode(WebSocketSession session, GraphQlWebSocketMessage message) {
|
||||
|
||||
DataBuffer buffer = ((Encoder<T>) this.encoder).encodeValue(
|
||||
(T) message, session.bufferFactory(), MESSAGE_TYPE, MimeTypeUtils.APPLICATION_JSON, null);
|
||||
@@ -108,7 +108,7 @@ final class CodecDelegate {
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) {
|
||||
GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) {
|
||||
DataBuffer buffer = DataBufferUtils.retain(webSocketMessage.getPayload());
|
||||
return (GraphQlWebSocketMessage) this.decoder.decode(buffer, MESSAGE_TYPE, null, null);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.lang.Nullable;
|
||||
* Default implementation of {@link ClientGraphQlRequest}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultClientGraphQlRequest extends DefaultGraphQlRequest implements ClientGraphQlRequest {
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.graphql.GraphQlResponse;
|
||||
* Default implementation of {@link ClientGraphQlResponse}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultClientGraphQlResponse extends ResponseMapGraphQlResponse implements ClientGraphQlResponse {
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.springframework.util.MimeTypeUtils;
|
||||
* support for decoding.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultClientResponseField implements ClientResponseField {
|
||||
|
||||
@@ -55,7 +54,7 @@ final class DefaultClientResponseField implements ClientResponseField {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public boolean hasValue() {
|
||||
return (this.field.getValue() != null);
|
||||
@@ -76,7 +75,7 @@ final class DefaultClientResponseField implements ClientResponseField {
|
||||
return this.field.getValue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public ResponseError getError() {
|
||||
return this.field.getError();
|
||||
@@ -100,13 +99,13 @@ final class DefaultClientResponseField implements ClientResponseField {
|
||||
@Override
|
||||
public <D> List<D> toEntityList(Class<D> elementType) {
|
||||
List<D> list = toEntity(ResolvableType.forClassWithGenerics(List.class, elementType));
|
||||
return (list != null ? list : Collections.emptyList());
|
||||
return (list != null) ? list : Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> List<D> toEntityList(ParameterizedTypeReference<D> elementType) {
|
||||
List<D> list = toEntity(ResolvableType.forClassWithGenerics(List.class, ResolvableType.forType(elementType)));
|
||||
return (list != null ? list : Collections.emptyList());
|
||||
return (list != null) ? list : Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.springframework.util.Assert;
|
||||
* Default, final {@link GraphQlClient} implementation for use with any transport.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultGraphQlClient implements GraphQlClient {
|
||||
|
||||
@@ -155,22 +154,22 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
|
||||
@Override
|
||||
public Mono<ClientGraphQlResponse> execute() {
|
||||
return initRequest().flatMap(request -> executeChain.next(request)
|
||||
return initRequest().flatMap((request) -> DefaultGraphQlClient.this.executeChain.next(request)
|
||||
.onErrorResume(
|
||||
ex -> !(ex instanceof GraphQlClientException),
|
||||
ex -> Mono.error(new GraphQlTransportException(ex, request))));
|
||||
(ex) -> !(ex instanceof GraphQlClientException),
|
||||
(ex) -> Mono.error(new GraphQlTransportException(ex, request))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ClientGraphQlResponse> executeSubscription() {
|
||||
return initRequest().flatMapMany(request -> executeSubscriptionChain.next(request)
|
||||
return initRequest().flatMapMany((request) -> DefaultGraphQlClient.this.executeSubscriptionChain.next(request)
|
||||
.onErrorResume(
|
||||
ex -> !(ex instanceof GraphQlClientException),
|
||||
ex -> Mono.error(new GraphQlTransportException(ex, request))));
|
||||
(ex) -> !(ex instanceof GraphQlClientException),
|
||||
(ex) -> Mono.error(new GraphQlTransportException(ex, request))));
|
||||
}
|
||||
|
||||
private Mono<ClientGraphQlRequest> initRequest() {
|
||||
return this.documentMono.map(document ->
|
||||
return this.documentMono.map((document) ->
|
||||
new DefaultClientGraphQlRequest(document, this.operationName, this.variables, this.extensions, this.attributes));
|
||||
}
|
||||
|
||||
@@ -198,7 +197,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
throw new FieldAccessException(
|
||||
((DefaultClientGraphQlResponse) response).getRequest(), response, field);
|
||||
}
|
||||
return (field.getValue() != null ? field : null);
|
||||
return (field.getValue() != null) ? field : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -215,27 +214,27 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
|
||||
@Override
|
||||
public <D> Mono<D> toEntity(Class<D> entityType) {
|
||||
return this.responseMono.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
return this.responseMono.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Mono<D> toEntity(ParameterizedTypeReference<D> entityType) {
|
||||
return this.responseMono.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
return this.responseMono.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Mono<List<D>> toEntityList(Class<D> elementType) {
|
||||
return this.responseMono.map(response -> {
|
||||
return this.responseMono.map((response) -> {
|
||||
ClientResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Mono<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
|
||||
return this.responseMono.map(response -> {
|
||||
return this.responseMono.map((response) -> {
|
||||
ClientResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -253,27 +252,27 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
|
||||
@Override
|
||||
public <D> Flux<D> toEntity(Class<D> entityType) {
|
||||
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Flux<D> toEntity(ParameterizedTypeReference<D> entityType) {
|
||||
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Flux<List<D>> toEntityList(Class<D> elementType) {
|
||||
return this.responseFlux.map(response -> {
|
||||
return this.responseFlux.map((response) -> {
|
||||
ClientResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Flux<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
|
||||
return this.responseFlux.map(response -> {
|
||||
return this.responseFlux.map((response) -> {
|
||||
ClientResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* around a {@link WebClient.Builder}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultHttpGraphQlClientBuilder
|
||||
extends AbstractGraphQlClientBuilder<DefaultHttpGraphQlClientBuilder>
|
||||
@@ -105,7 +104,7 @@ final class DefaultHttpGraphQlClientBuilder
|
||||
public HttpGraphQlClient build() {
|
||||
|
||||
// Pass the codecs to the parent for response decoding
|
||||
this.webClientBuilder.codecs(configurer ->
|
||||
this.webClientBuilder.codecs((configurer) ->
|
||||
setJsonCodecs(
|
||||
CodecDelegate.findJsonEncoder(configurer),
|
||||
CodecDelegate.findJsonDecoder(configurer)));
|
||||
@@ -139,6 +138,7 @@ final class DefaultHttpGraphQlClientBuilder
|
||||
this.builderInitializer = builderInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultHttpGraphQlClientBuilder mutate() {
|
||||
DefaultHttpGraphQlClientBuilder builder = new DefaultHttpGraphQlClientBuilder(this.webClient);
|
||||
this.builderInitializer.accept(builder);
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.springframework.util.MimeTypeUtils;
|
||||
* a {@link RSocketRequester.Builder}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultRSocketGraphQlClientBuilder
|
||||
extends AbstractGraphQlClientBuilder<DefaultRSocketGraphQlClientBuilder>
|
||||
@@ -134,9 +133,9 @@ final class DefaultRSocketGraphQlClientBuilder
|
||||
public RSocketGraphQlClient build() {
|
||||
|
||||
// Pass the codecs to the parent for response decoding
|
||||
this.requesterBuilder.rsocketStrategies(builder -> {
|
||||
builder.decoders(decoders -> setJsonDecoder(CodecDelegate.findJsonDecoder(decoders)));
|
||||
builder.encoders(encoders -> setJsonEncoder(CodecDelegate.findJsonEncoder(encoders)));
|
||||
this.requesterBuilder.rsocketStrategies((builder) -> {
|
||||
builder.decoders((decoders) -> setJsonDecoder(CodecDelegate.findJsonDecoder(decoders)));
|
||||
builder.encoders((encoders) -> setJsonEncoder(CodecDelegate.findJsonEncoder(encoders)));
|
||||
});
|
||||
|
||||
RSocketRequester requester;
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.util.Assert;
|
||||
* Default {@link GraphQlClient.Builder} with a given, externally, prepared transport.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultTransportGraphQlClientBuilder
|
||||
extends AbstractGraphQlClientBuilder<DefaultTransportGraphQlClientBuilder> {
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -37,7 +36,6 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
* {@code WebSocketGraphQlTransport}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultWebSocketGraphQlClientBuilder
|
||||
extends AbstractGraphQlClientBuilder<DefaultWebSocketGraphQlClientBuilder>
|
||||
@@ -130,14 +128,14 @@ final class DefaultWebSocketGraphQlClientBuilder
|
||||
private WebSocketGraphQlClientInterceptor getInterceptor() {
|
||||
|
||||
List<WebSocketGraphQlClientInterceptor> interceptors = getInterceptors().stream()
|
||||
.filter(interceptor -> interceptor instanceof WebSocketGraphQlClientInterceptor)
|
||||
.map(interceptor -> (WebSocketGraphQlClientInterceptor) interceptor)
|
||||
.filter((interceptor) -> interceptor instanceof WebSocketGraphQlClientInterceptor)
|
||||
.map((interceptor) -> (WebSocketGraphQlClientInterceptor) interceptor)
|
||||
.toList();
|
||||
|
||||
Assert.state(interceptors.size() <= 1,
|
||||
"Only a single interceptor of type WebSocketGraphQlClientInterceptor may be configured");
|
||||
|
||||
return (!interceptors.isEmpty() ? interceptors.get(0) : new WebSocketGraphQlClientInterceptor() {});
|
||||
return (!interceptors.isEmpty() ? interceptors.get(0) : new WebSocketGraphQlClientInterceptor() { });
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ public class FieldAccessException extends GraphQlClientException {
|
||||
|
||||
/**
|
||||
* Constructor with the request and response, and the accessed field.
|
||||
* @param request the client request
|
||||
* @param response the client response
|
||||
* @param field the accessed field that caused the error
|
||||
*/
|
||||
public FieldAccessException(
|
||||
ClientGraphQlRequest request, ClientGraphQlResponse response, ClientResponseField field) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.client;
|
||||
|
||||
import java.util.List;
|
||||
@@ -61,6 +62,7 @@ public interface GraphQlClient {
|
||||
* Variant of {@link #document(String)} that uses the given key to resolve
|
||||
* the GraphQL document from a file with the help of the configured
|
||||
* {@link Builder#documentSource(DocumentSource) DocumentSource}.
|
||||
* @param name the document name
|
||||
* @throws IllegalArgumentException if the content could not be loaded
|
||||
*/
|
||||
RequestSpec documentName(String name);
|
||||
@@ -87,6 +89,7 @@ public interface GraphQlClient {
|
||||
|
||||
/**
|
||||
* Defines a builder for creating {@link GraphQlClient} instances.
|
||||
* @param <B> the client builder type
|
||||
*/
|
||||
interface Builder<B extends Builder<B>> {
|
||||
|
||||
@@ -112,6 +115,7 @@ public interface GraphQlClient {
|
||||
* <p>By default, this is set to {@link ResourceDocumentSource} with
|
||||
* classpath location {@code "graphql-documents/"} and
|
||||
* {@link ResourceDocumentSource#FILE_EXTENSIONS} as extensions.
|
||||
* @param contentLoader the document source
|
||||
*/
|
||||
B documentSource(DocumentSource contentLoader);
|
||||
|
||||
@@ -190,6 +194,7 @@ public interface GraphQlClient {
|
||||
* <pre>
|
||||
* client.document("..").execute().map(response -> response.toEntity(..))
|
||||
* </pre>
|
||||
* @param path the field path
|
||||
* @return a spec with decoding options
|
||||
* @throws FieldAccessException if the field has any field errors,
|
||||
* including errors at, above or below the field path.
|
||||
@@ -203,6 +208,7 @@ public interface GraphQlClient {
|
||||
* <pre>
|
||||
* client.document("..").executeSubscription().map(response -> response.toEntity(..))
|
||||
* </pre>
|
||||
* @param path the field path
|
||||
* @return a spec with decoding options
|
||||
*/
|
||||
RetrieveSubscriptionSpec retrieveSubscription(String path);
|
||||
@@ -242,6 +248,7 @@ public interface GraphQlClient {
|
||||
|
||||
/**
|
||||
* Decode the field to an entity of the given type.
|
||||
* @param <D> the entity type
|
||||
* @param entityType the type to convert to
|
||||
* @return {@code Mono} with the decoded entity; completes with
|
||||
* {@link FieldAccessException} in case of {@link ResponseField field
|
||||
@@ -253,17 +260,21 @@ public interface GraphQlClient {
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
* @param <D> the entity type
|
||||
* @param entityType the type to convert to
|
||||
*/
|
||||
<D> Mono<D> toEntity(ParameterizedTypeReference<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a List of entities.
|
||||
* @param <D> the entity type
|
||||
* @param elementType the type of elements in the list
|
||||
*/
|
||||
<D> Mono<List<D>> toEntityList(Class<D> elementType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a List of entities.
|
||||
* @param <D> the entity type
|
||||
* @param elementType the type of elements in the list
|
||||
*/
|
||||
<D> Mono<List<D>> toEntityList(ParameterizedTypeReference<D> elementType);
|
||||
@@ -278,6 +289,7 @@ public interface GraphQlClient {
|
||||
|
||||
/**
|
||||
* Decode the field to an entity of the given type.
|
||||
* @param <D> the entity type
|
||||
* @param entityType the type to convert to
|
||||
* @return {@code Mono} with the decoded entity; completes with
|
||||
* {@link FieldAccessException} in case of {@link ResponseField field
|
||||
@@ -289,20 +301,25 @@ public interface GraphQlClient {
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
* @param <D> the entity type
|
||||
* @param entityType the type to convert to
|
||||
*/
|
||||
<D> Flux<D> toEntity(ParameterizedTypeReference<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode each response to a List of entities.
|
||||
* @param <D> the entity type
|
||||
* @param elementType the type of elements in the list
|
||||
*/
|
||||
<D> Flux<List<D>> toEntityList(Class<D> elementType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode each response to a List of entities.
|
||||
* @param <D> the entity type
|
||||
* @param elementType the type of elements in the list
|
||||
*/
|
||||
<D> Flux<List<D>> toEntityList(ParameterizedTypeReference<D> elementType);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ public class GraphQlClientException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor with a message, optional cause, and the request details.
|
||||
* @param message the exception message to use
|
||||
* @param cause the original cause for the client exception
|
||||
* @param request the request that failed
|
||||
*/
|
||||
public GraphQlClientException(String message, @Nullable Throwable cause, GraphQlRequest request) {
|
||||
super(message, cause);
|
||||
|
||||
@@ -65,13 +65,13 @@ public interface GraphQlClientInterceptor {
|
||||
@Override
|
||||
public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
|
||||
return GraphQlClientInterceptor.this.intercept(
|
||||
request, nextRequest -> interceptor.intercept(nextRequest, chain));
|
||||
request, (nextRequest) -> interceptor.intercept(nextRequest, chain));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
|
||||
return GraphQlClientInterceptor.this.interceptSubscription(
|
||||
request, nextRequest -> interceptor.interceptSubscription(nextRequest, chain));
|
||||
request, (nextRequest) -> interceptor.interceptSubscription(nextRequest, chain));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public interface GraphQlTransport {
|
||||
/**
|
||||
* Factory method to create {@link GraphQlResponse} from a GraphQL response
|
||||
* map for use in transport implementations.
|
||||
* @param responseMap the GraphQL response map
|
||||
*/
|
||||
static GraphQlResponse createResponse(Map<String, Object> responseMap) {
|
||||
return new ResponseMapGraphQlResponse(responseMap);
|
||||
|
||||
@@ -32,6 +32,8 @@ public class GraphQlTransportException extends GraphQlClientException {
|
||||
|
||||
/**
|
||||
* Constructor with a default message.
|
||||
* @param cause the original cause of the transport error
|
||||
* @param request the request that failed at the transport level
|
||||
*/
|
||||
public GraphQlTransportException(@Nullable Throwable cause, GraphQlRequest request) {
|
||||
super("GraphQlTransport error: " + cause.getMessage(), cause, request);
|
||||
@@ -39,6 +41,9 @@ public class GraphQlTransportException extends GraphQlClientException {
|
||||
|
||||
/**
|
||||
* Constructor with a given message.
|
||||
* @param message the exception message to use
|
||||
* @param cause the original cause of the transport error
|
||||
* @param request the request that failed at the transport level
|
||||
*/
|
||||
public GraphQlTransportException(String message, @Nullable Throwable cause, GraphQlRequest request) {
|
||||
super(message, cause, request);
|
||||
|
||||
@@ -36,6 +36,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
|
||||
|
||||
/**
|
||||
* Create an {@link HttpGraphQlClient} that uses the given {@link WebClient}.
|
||||
* @param webClient the {@code WebClient} to use for sending HTTP requests
|
||||
*/
|
||||
static HttpGraphQlClient create(WebClient webClient) {
|
||||
return builder(webClient.mutate()).build();
|
||||
@@ -51,6 +52,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
|
||||
/**
|
||||
* Variant of {@link #builder()} with a pre-configured {@code WebClient}
|
||||
* to mutate and customize further through the returned builder.
|
||||
* @param webClient the {@code WebClient} to use for sending HTTP requests
|
||||
*/
|
||||
static Builder<?> builder(WebClient webClient) {
|
||||
return builder(webClient.mutate());
|
||||
@@ -59,6 +61,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
|
||||
/**
|
||||
* Variant of {@link #builder()} with a pre-configured {@code WebClient}
|
||||
* to mutate and customize further through the returned builder.
|
||||
* @param webClientBuilder the {@code WebClient.Builder} to use for building the HTTP client
|
||||
*/
|
||||
static Builder<?> builder(WebClient.Builder webClientBuilder) {
|
||||
return new DefaultHttpGraphQlClientBuilder(webClientBuilder);
|
||||
@@ -67,6 +70,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
|
||||
|
||||
/**
|
||||
* Builder for the GraphQL over HTTP client.
|
||||
* @param <B> the builder type
|
||||
*/
|
||||
interface Builder<B extends Builder<B>> extends WebGraphQlClient.Builder<B> {
|
||||
|
||||
@@ -74,6 +78,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
|
||||
* Customize the {@code WebClient} to use.
|
||||
* <p>Note that some properties of {@code WebClient.Builder} like the
|
||||
* base URL, headers, and codecs can be customized through this builder.
|
||||
* @param webClient the function for customizing the {@code WebClient.Builder} that's used to build the HTTP client
|
||||
* @see #url(String)
|
||||
* @see #header(String, String...)
|
||||
* @see #codecConfigurer(Consumer)
|
||||
|
||||
@@ -37,12 +37,11 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
* see {@link WebSocketGraphQlTransport} and {@link RSocketGraphQlTransport}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class HttpGraphQlTransport implements GraphQlTransport {
|
||||
|
||||
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE =
|
||||
new ParameterizedTypeReference<Map<String, Object>>() {};
|
||||
new ParameterizedTypeReference<Map<String, Object>>() { };
|
||||
|
||||
// To be removed in favor of Framework's MediaType.APPLICATION_GRAPHQL_RESPONSE
|
||||
private static final MediaType APPLICATION_GRAPHQL_RESPONSE =
|
||||
@@ -64,7 +63,7 @@ final class HttpGraphQlTransport implements GraphQlTransport {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
webClient.mutate().defaultHeaders(headers::putAll);
|
||||
MediaType contentType = headers.getContentType();
|
||||
return (contentType != null ? contentType : MediaType.APPLICATION_JSON);
|
||||
return (contentType != null) ? contentType : MediaType.APPLICATION_JSON;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +74,7 @@ final class HttpGraphQlTransport implements GraphQlTransport {
|
||||
.contentType(this.contentType)
|
||||
.accept(MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_GRAPHQL)
|
||||
.bodyValue(request.toMap())
|
||||
.attributes(attributes -> {
|
||||
.attributes((attributes) -> {
|
||||
if (request instanceof ClientGraphQlRequest clientRequest) {
|
||||
attributes.putAll(clientRequest.getAttributes());
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public interface RSocketGraphQlClient extends GraphQlClient {
|
||||
|
||||
/**
|
||||
* Start with a given {@link #builder()}.
|
||||
* @param requesterBuilder the existing request builder
|
||||
*/
|
||||
static Builder<?> builder(RSocketRequester.Builder requesterBuilder) {
|
||||
return new DefaultRSocketGraphQlClientBuilder(requesterBuilder);
|
||||
@@ -78,6 +79,7 @@ public interface RSocketGraphQlClient extends GraphQlClient {
|
||||
|
||||
/**
|
||||
* Builder for the GraphQL over HTTP client.
|
||||
* @param <B> the builder type
|
||||
*/
|
||||
interface Builder<B extends Builder<B>> extends GraphQlClient.Builder<B> {
|
||||
|
||||
@@ -146,11 +148,12 @@ public interface RSocketGraphQlClient extends GraphQlClient {
|
||||
* <p>Note that some properties of {@code RSocketRequester.Builder} like the
|
||||
* data MimeType, and the underlying RSocket transport can be customized
|
||||
* through this builder.
|
||||
* @param requester the requester to be customized
|
||||
* @return the same builder instance
|
||||
* @see #dataMimeType(MimeType)
|
||||
* @see #tcp(String, int)
|
||||
* @see #webSocket(URI)
|
||||
* @see #clientTransport(ClientTransport)
|
||||
* @return the same builder instance
|
||||
*/
|
||||
B rsocketRequester(Consumer<RSocketRequester.Builder> requester);
|
||||
|
||||
|
||||
@@ -45,12 +45,11 @@ import org.springframework.util.Assert;
|
||||
* metadata extension.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class RSocketGraphQlTransport implements GraphQlTransport {
|
||||
|
||||
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE =
|
||||
new ParameterizedTypeReference<Map<String, Object>>() {};
|
||||
new ParameterizedTypeReference<Map<String, Object>>() { };
|
||||
|
||||
private static final ResolvableType LIST_TYPE = ResolvableType.forClass(List.class);
|
||||
|
||||
@@ -83,7 +82,7 @@ final class RSocketGraphQlTransport implements GraphQlTransport {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
return this.rsocketRequester.route(this.route).data(request.toMap())
|
||||
.retrieveFlux(MAP_TYPE)
|
||||
.onErrorResume(RejectedException.class, ex -> Flux.error(decodeErrors(request, ex)))
|
||||
.onErrorResume(RejectedException.class, (ex) -> Flux.error(decodeErrors(request, ex)))
|
||||
.map(ResponseMapGraphQlResponse::new);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.util.ObjectUtils;
|
||||
* {@link GraphQlResponse} that wraps a deserialized the GraphQL response map.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
|
||||
|
||||
@@ -60,7 +59,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<ResponseError> wrapErrors(Map<String, Object> map) {
|
||||
List<Map<String, Object>> errors = (List<Map<String, Object>>) map.get("errors");
|
||||
errors = (errors != null ? errors : Collections.emptyList());
|
||||
errors = (errors != null) ? errors : Collections.emptyList();
|
||||
return errors.stream().map(MapResponseError::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -134,7 +133,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return locations.stream()
|
||||
.map(m -> new SourceLocation(getInt(m, "line"), getInt(m, "column"), (String) m.get("sourceName")))
|
||||
.map((m) -> new SourceLocation(getInt(m, "line"), getInt(m, "column"), (String) m.get("sourceName")))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -155,7 +154,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
|
||||
return "";
|
||||
}
|
||||
return path.stream().reduce("",
|
||||
(s, o) -> s + (o instanceof Integer ? "[" + o + "]" : (s.isEmpty() ? o : "." + o)),
|
||||
(s, o) -> s + ((o instanceof Integer) ? "[" + o + "]" : (s.isEmpty() ? o : "." + o)),
|
||||
(s, s2) -> null);
|
||||
}
|
||||
|
||||
@@ -163,7 +162,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMessage() {
|
||||
return (String) errorMap.get("message");
|
||||
return (String) this.errorMap.get("message");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,6 +38,8 @@ public class SubscriptionErrorException extends GraphQlTransportException {
|
||||
/**
|
||||
* Constructor with the request details and the errors listed in the payload
|
||||
* of the {@code "errors"} message.
|
||||
* @param request the request details
|
||||
* @param errors the errors listed in the payload
|
||||
*/
|
||||
public SubscriptionErrorException(GraphQlRequest request, List<ResponseError> errors) {
|
||||
super("GraphQL subscription completed with an \"error\" message, " +
|
||||
|
||||
@@ -39,6 +39,7 @@ public interface WebGraphQlClient extends GraphQlClient {
|
||||
|
||||
/**
|
||||
* Base builder for GraphQL clients over a Web transport.
|
||||
* @param <B> the builder type
|
||||
*/
|
||||
interface Builder<B extends Builder<B>> extends GraphQlClient.Builder<B> {
|
||||
|
||||
@@ -71,6 +72,7 @@ public interface WebGraphQlClient extends GraphQlClient {
|
||||
/**
|
||||
* Configure the underlying {@code CodecConfigurer} to use for all JSON
|
||||
* encoding and decoding needs.
|
||||
* @param codecsConsumer a consumer that configures the {@code CodecConfigurer}
|
||||
*/
|
||||
B codecConfigurer(Consumer<CodecConfigurer> codecsConsumer);
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ public class WebSocketDisconnectedException extends GraphQlTransportException {
|
||||
/**
|
||||
* Constructor with an explanation about the closure, along with the request
|
||||
* details and the status used to close the WebSocket session.
|
||||
* @param closeStatusMessage the message received when the connection was closed
|
||||
* @param request the ongoing request when the connection was closed
|
||||
* @param status the received close status
|
||||
*/
|
||||
public WebSocketDisconnectedException(String closeStatusMessage, GraphQlRequest request, CloseStatus status) {
|
||||
super(closeStatusMessage, null, request);
|
||||
|
||||
@@ -85,6 +85,7 @@ public interface WebSocketGraphQlClient extends WebGraphQlClient {
|
||||
|
||||
/**
|
||||
* Builder for a GraphQL over WebSocket client.
|
||||
* @param <B> the builder type
|
||||
*/
|
||||
interface Builder<B extends Builder<B>> extends WebGraphQlClient.Builder<B> {
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.client;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -50,7 +51,6 @@ import org.springframework.web.reactive.socket.client.WebSocketClient;
|
||||
* {@link GraphQlTransport} for GraphQL over WebSocket via {@link WebSocketClient}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
* @see <a href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md">GraphQL over WebSocket protocol</a>
|
||||
*/
|
||||
final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
@@ -78,7 +78,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
Assert.notNull(interceptor, "WebSocketGraphQlClientInterceptor is required");
|
||||
|
||||
this.url = url;
|
||||
this.headers.putAll(headers != null ? headers : HttpHeaders.EMPTY);
|
||||
this.headers.putAll((headers != null) ? headers : HttpHeaders.EMPTY);
|
||||
this.webSocketClient = client;
|
||||
|
||||
this.graphQlSessionHandler = new GraphQlSessionHandler(codecConfigurer, interceptor);
|
||||
@@ -100,7 +100,9 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
Mono<GraphQlSession> sessionMono = handler.getGraphQlSession();
|
||||
|
||||
client.execute(uri, headers, handler)
|
||||
.subscribe(aVoid -> {},
|
||||
.subscribe((aVoid) -> {
|
||||
|
||||
},
|
||||
handler::handleWebSocketSessionError,
|
||||
handler::handleWebSocketSessionClosed);
|
||||
|
||||
@@ -109,19 +111,19 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}
|
||||
|
||||
|
||||
public URI getUrl() {
|
||||
URI getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
public WebSocketClient getWebSocketClient() {
|
||||
WebSocketClient getWebSocketClient() {
|
||||
return this.webSocketClient;
|
||||
}
|
||||
|
||||
public CodecConfigurer getCodecConfigurer() {
|
||||
CodecConfigurer getCodecConfigurer() {
|
||||
return this.graphQlSessionHandler.getCodecConfigurer();
|
||||
}
|
||||
|
||||
@@ -132,7 +134,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* @return {@code Mono} that completes when the WebSocket is connected and
|
||||
* ready to begin sending GraphQL requests
|
||||
*/
|
||||
public Mono<Void> start() {
|
||||
Mono<Void> start() {
|
||||
this.graphQlSessionHandler.setStopped(false);
|
||||
return this.graphQlSessionMono.then();
|
||||
}
|
||||
@@ -145,19 +147,19 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* call {@link #start()} to allow requests again.
|
||||
* @return {@code Mono} that completes when the underlying session is closed
|
||||
*/
|
||||
public Mono<Void> stop() {
|
||||
Mono<Void> stop() {
|
||||
this.graphQlSessionHandler.setStopped(true);
|
||||
return this.graphQlSessionMono.flatMap(GraphQlSession::close).onErrorResume(ex -> Mono.empty());
|
||||
return this.graphQlSessionMono.flatMap(GraphQlSession::close).onErrorResume((ex) -> Mono.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
return this.graphQlSessionMono.flatMap(session -> session.execute(request));
|
||||
return this.graphQlSessionMono.flatMap((session) -> session.execute(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
return this.graphQlSessionMono.flatMapMany(session -> session.executeSubscription(request));
|
||||
return this.graphQlSessionMono.flatMapMany((session) -> session.executeSubscription(request));
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +191,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}
|
||||
|
||||
|
||||
public CodecConfigurer getCodecConfigurer() {
|
||||
CodecConfigurer getCodecConfigurer() {
|
||||
return this.codecDelegate.getCodecConfigurer();
|
||||
}
|
||||
|
||||
@@ -205,7 +207,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* the "connection_init" and "connection_ack" messages are exchanged or
|
||||
* returns an error if it fails for any reason.
|
||||
*/
|
||||
public Mono<GraphQlSession> getGraphQlSession() {
|
||||
Mono<GraphQlSession> getGraphQlSession() {
|
||||
return this.graphQlSessionSink.asMono();
|
||||
}
|
||||
|
||||
@@ -213,14 +215,14 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* When the handler is marked "stopped", i.e. set to {@code true}, new
|
||||
* requests are rejected. When set to {@code true} they are allowed.
|
||||
*/
|
||||
public void setStopped(boolean stopped) {
|
||||
void setStopped(boolean stopped) {
|
||||
this.stopped.set(stopped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the handler is marked {@link #setStopped(boolean) "stopped"}.
|
||||
*/
|
||||
public boolean isStopped() {
|
||||
boolean isStopped() {
|
||||
return this.stopped.get();
|
||||
}
|
||||
|
||||
@@ -241,10 +243,10 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
|
||||
Mono<Void> sendCompletion =
|
||||
session.send(connectionInitMono.concatWith(graphQlSession.getRequestFlux())
|
||||
.map(message -> this.codecDelegate.encode(session, message)));
|
||||
.map((message) -> this.codecDelegate.encode(session, message)));
|
||||
|
||||
Mono<Void> receiveCompletion = session.receive()
|
||||
.flatMap(webSocketMessage -> {
|
||||
.flatMap((webSocketMessage) -> {
|
||||
if (sessionNotInitialized()) {
|
||||
try {
|
||||
GraphQlWebSocketMessage message = this.codecDelegate.decode(webSocketMessage);
|
||||
@@ -301,14 +303,14 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
private void registerCloseStatusHandling(GraphQlSession graphQlSession, WebSocketSession session) {
|
||||
session.closeStatus()
|
||||
.defaultIfEmpty(CloseStatus.NO_STATUS_CODE)
|
||||
.doOnNext(closeStatus -> {
|
||||
.doOnNext((closeStatus) -> {
|
||||
String closeStatusMessage = initCloseStatusMessage(closeStatus, null, graphQlSession);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(closeStatusMessage);
|
||||
}
|
||||
graphQlSession.terminateRequests(closeStatusMessage, closeStatus);
|
||||
})
|
||||
.doOnError(cause -> {
|
||||
.doOnError((cause) -> {
|
||||
CloseStatus closeStatus = CloseStatus.NO_STATUS_CODE;
|
||||
String closeStatusMessage = initCloseStatusMessage(closeStatus, cause, graphQlSession);
|
||||
if (logger.isErrorEnabled()) {
|
||||
@@ -347,7 +349,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* with an error. The error is routed to subscribers of
|
||||
* {@link #getGraphQlSession()} which is necessary for connection issues.
|
||||
*/
|
||||
public void handleWebSocketSessionError(Throwable ex) {
|
||||
void handleWebSocketSessionError(Throwable ex) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Session handling error: " + ex.getMessage(), ex);
|
||||
@@ -364,7 +366,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* This must be called from code that calls the {@code WebSocketClient}
|
||||
* when execution completes.
|
||||
*/
|
||||
public void handleWebSocketSessionClosed() {
|
||||
void handleWebSocketSessionClosed() {
|
||||
this.graphQlSessionSink = Sinks.unsafe().one();
|
||||
}
|
||||
|
||||
@@ -396,16 +398,16 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
/**
|
||||
* Return the {@code Flux} of GraphQL requests to send as WebSocket messages.
|
||||
*/
|
||||
public Flux<GraphQlWebSocketMessage> getRequestFlux() {
|
||||
Flux<GraphQlWebSocketMessage> getRequestFlux() {
|
||||
return this.requestSink.getRequestFlux();
|
||||
}
|
||||
|
||||
|
||||
// Outbound messages
|
||||
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
String id = String.valueOf(this.requestIndex.incrementAndGet());
|
||||
return Mono.<GraphQlResponse>create(sink -> {
|
||||
return Mono.<GraphQlResponse>create((sink) -> {
|
||||
SingleResponseRequestState state = new SingleResponseRequestState(request, sink);
|
||||
this.requestStateMap.put(id, state);
|
||||
try {
|
||||
@@ -419,9 +421,9 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}).doOnCancel(() -> this.requestStateMap.remove(id));
|
||||
}
|
||||
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
String id = String.valueOf(this.requestIndex.incrementAndGet());
|
||||
return Flux.<GraphQlResponse>create(sink -> {
|
||||
return Flux.<GraphQlResponse>create((sink) -> {
|
||||
SubscriptionRequestState state = new SubscriptionRequestState(request, sink);
|
||||
this.requestStateMap.put(id, state);
|
||||
try {
|
||||
@@ -452,7 +454,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPong(@Nullable Map<String, Object> payload) {
|
||||
void sendPong(@Nullable Map<String, Object> payload) {
|
||||
GraphQlWebSocketMessage message = GraphQlWebSocketMessage.pong(payload);
|
||||
this.requestSink.sendRequest(message);
|
||||
}
|
||||
@@ -463,7 +465,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
/**
|
||||
* Handle a "next" message and route to its recipient.
|
||||
*/
|
||||
public void handleNext(GraphQlWebSocketMessage message) {
|
||||
void handleNext(GraphQlWebSocketMessage message) {
|
||||
String id = message.getId();
|
||||
RequestState requestState = this.requestStateMap.get(id);
|
||||
if (requestState == null) {
|
||||
@@ -486,7 +488,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* Handle an "error" message, turning it into an {@link GraphQlResponse}
|
||||
* for single responses, or signaling an error for streams.
|
||||
*/
|
||||
public void handleError(GraphQlWebSocketMessage message) {
|
||||
void handleError(GraphQlWebSocketMessage message) {
|
||||
String id = message.getId();
|
||||
RequestState requestState = this.requestStateMap.remove(id);
|
||||
if (requestState == null) {
|
||||
@@ -512,7 +514,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
/**
|
||||
* Handle a "complete" message.
|
||||
*/
|
||||
public void handleComplete(GraphQlWebSocketMessage message) {
|
||||
void handleComplete(GraphQlWebSocketMessage message) {
|
||||
String id = message.getId();
|
||||
RequestState requestState = this.requestStateMap.remove(id);
|
||||
if (requestState == null) {
|
||||
@@ -528,22 +530,22 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* Return a {@code Mono} that completes when the connection is closed
|
||||
* for any reason.
|
||||
*/
|
||||
public Mono<Void> notifyWhenClosed() {
|
||||
Mono<Void> notifyWhenClosed() {
|
||||
return this.connection.notifyWhenClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the underlying connection.
|
||||
*/
|
||||
public Mono<Void> close() {
|
||||
Mono<Void> close() {
|
||||
return this.connection.close(CloseStatus.GOING_AWAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate and clean all in-progress requests with the given error.
|
||||
*/
|
||||
public void terminateRequests(String message, CloseStatus status) {
|
||||
this.requestStateMap.values().forEach(info -> info.emitDisconnectError(message, status));
|
||||
void terminateRequests(String message, CloseStatus status) {
|
||||
this.requestStateMap.values().forEach((info) -> info.emitDisconnectError(message, status));
|
||||
this.requestStateMap.clear();
|
||||
}
|
||||
|
||||
@@ -595,21 +597,21 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
/**
|
||||
* Holds the request {@code Flux} and associated {@link FluxSink}.
|
||||
*/
|
||||
private static class RequestSink {
|
||||
private static final class RequestSink {
|
||||
|
||||
@Nullable
|
||||
private FluxSink<GraphQlWebSocketMessage> requestSink;
|
||||
|
||||
private final Flux<GraphQlWebSocketMessage> requestFlux = Flux.create(sink -> {
|
||||
private final Flux<GraphQlWebSocketMessage> requestFlux = Flux.create((sink) -> {
|
||||
Assert.state(this.requestSink == null, "Expected single subscriber only for outbound messages");
|
||||
this.requestSink = sink;
|
||||
});
|
||||
|
||||
public Flux<GraphQlWebSocketMessage> getRequestFlux() {
|
||||
Flux<GraphQlWebSocketMessage> getRequestFlux() {
|
||||
return this.requestFlux;
|
||||
}
|
||||
|
||||
public void sendRequest(GraphQlWebSocketMessage message) {
|
||||
void sendRequest(GraphQlWebSocketMessage message) {
|
||||
Assert.state(this.requestSink != null, "Unexpected request before Flux is subscribed to");
|
||||
this.requestSink.next(message);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data;
|
||||
|
||||
|
||||
@@ -39,8 +40,8 @@ import org.springframework.util.ObjectUtils;
|
||||
* object.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @param <T> the type of value contained
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.1.0
|
||||
* @see <a href="http://spec.graphql.org/October2021/#sec-Non-Null.Nullable-vs-Optional">Nullable vs Optional</a>
|
||||
*/
|
||||
@@ -115,6 +116,7 @@ public final class ArgumentValue<T> {
|
||||
/**
|
||||
* Static factory method for an argument value that was provided, even if
|
||||
* it was set to {@literal "null}.
|
||||
* @param <T> the type of value
|
||||
* @param value the value to hold in the instance
|
||||
*/
|
||||
public static <T> ArgumentValue<T> ofNullable(@Nullable T value) {
|
||||
@@ -123,6 +125,7 @@ public final class ArgumentValue<T> {
|
||||
|
||||
/**
|
||||
* Static factory method for an argument value that was omitted.
|
||||
* @param <T> the type of value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ArgumentValue<T> omitted() {
|
||||
|
||||
@@ -139,7 +139,7 @@ public class GraphQlArgumentBinder {
|
||||
DataFetchingEnvironment environment, @Nullable String name, ResolvableType targetType)
|
||||
throws BindException {
|
||||
|
||||
Object rawValue = (name != null ? environment.getArgument(name) : environment.getArguments());
|
||||
Object rawValue = (name != null) ? environment.getArgument(name) : environment.getArguments();
|
||||
boolean isOmitted = (name != null && !environment.getArguments().containsKey(name));
|
||||
|
||||
ArgumentsBindingResult bindingResult = new ArgumentsBindingResult(targetType);
|
||||
@@ -246,9 +246,9 @@ public class GraphQlArgumentBinder {
|
||||
|
||||
Constructor<?> constructor = BeanUtils.getResolvableConstructor(targetClass);
|
||||
|
||||
Object value = (constructor.getParameterCount() > 0 ?
|
||||
Object value = (constructor.getParameterCount() > 0) ?
|
||||
bindMapToObjectViaConstructor(rawMap, constructor, targetType, bindingResult) :
|
||||
bindMapToObjectViaSetters(rawMap, constructor, targetType, bindingResult));
|
||||
bindMapToObjectViaSetters(rawMap, constructor, targetType, bindingResult);
|
||||
|
||||
bindingResult.popNestedPath();
|
||||
|
||||
@@ -360,7 +360,7 @@ public class GraphQlArgumentBinder {
|
||||
Object value = null;
|
||||
try {
|
||||
TypeConverter converter =
|
||||
(this.typeConverter != null ? this.typeConverter : new SimpleTypeConverter());
|
||||
(this.typeConverter != null) ? this.typeConverter : new SimpleTypeConverter();
|
||||
|
||||
value = converter.convertIfNecessary(
|
||||
rawValue, (Class<?>) clazz, new TypeDescriptor(type, null, null));
|
||||
@@ -385,9 +385,9 @@ public class GraphQlArgumentBinder {
|
||||
}
|
||||
|
||||
private static String initObjectName(ResolvableType targetType) {
|
||||
return (targetType.getSource() instanceof MethodParameter methodParameter ?
|
||||
return (targetType.getSource() instanceof MethodParameter methodParameter) ?
|
||||
Conventions.getVariableNameForParameter(methodParameter) :
|
||||
ClassUtils.getShortNameAsProperty(targetType.resolve(Object.class)));
|
||||
ClassUtils.getShortNameAsProperty(targetType.resolve(Object.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -400,7 +400,7 @@ public class GraphQlArgumentBinder {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void rejectArgumentValue(
|
||||
void rejectArgumentValue(
|
||||
String field, @Nullable Object rawValue, String code, String defaultMessage) {
|
||||
|
||||
addError(new FieldError(
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -77,6 +78,8 @@ public class HandlerMethod {
|
||||
|
||||
/**
|
||||
* Constructor with a handler instance and a method.
|
||||
* @param bean the handler instance
|
||||
* @param method the handler method
|
||||
*/
|
||||
public HandlerMethod(Object bean, Method method) {
|
||||
Assert.notNull(bean, "Bean is required");
|
||||
@@ -94,6 +97,9 @@ public class HandlerMethod {
|
||||
* Constructor with a bean name for the handler along with a {@code BeanFactory}
|
||||
* to allow {@link #createWithResolvedBean() resolving} the handler instance
|
||||
* later.
|
||||
* @param beanName the bean name
|
||||
* @param beanFactory the bean factory to use for bean resolution
|
||||
* @param method the handler method
|
||||
*/
|
||||
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
|
||||
Assert.hasText(beanName, "Bean name is required");
|
||||
@@ -115,6 +121,7 @@ public class HandlerMethod {
|
||||
|
||||
/**
|
||||
* Copy constructor for use from subclasses that accept more arguments.
|
||||
* @param handlerMethod the handler method
|
||||
*/
|
||||
protected HandlerMethod(HandlerMethod handlerMethod) {
|
||||
this(handlerMethod, handlerMethod.bean);
|
||||
@@ -191,6 +198,7 @@ public class HandlerMethod {
|
||||
|
||||
/**
|
||||
* Return the actual return value type.
|
||||
* @param returnValue the return value instance, can be {@code null}
|
||||
*/
|
||||
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
|
||||
return new ReturnValueMethodParameter(returnValue);
|
||||
@@ -208,6 +216,7 @@ public class HandlerMethod {
|
||||
* if no annotation can be found on the given method itself.
|
||||
* <p>Also supports <em>merged</em> composed annotations with attribute
|
||||
* overrides as of Spring Framework 4.3.
|
||||
* @param <A> the annotation type
|
||||
* @param annotationType the type of annotation to introspect the method for
|
||||
* @return the annotation, or {@code null} if none found
|
||||
* @see AnnotatedElementUtils#findMergedAnnotation
|
||||
@@ -219,6 +228,7 @@ public class HandlerMethod {
|
||||
|
||||
/**
|
||||
* Return whether the parameter is declared with the given annotation type.
|
||||
* @param <A> the annotation type
|
||||
* @param annotationType the annotation type to look for
|
||||
* @see AnnotatedElementUtils#hasAnnotation
|
||||
*/
|
||||
@@ -331,6 +341,9 @@ public class HandlerMethod {
|
||||
* processing time may be a JDK dynamic proxy (lazy initialization, prototype
|
||||
* beans, and others). Endpoint classes that require proxying should prefer
|
||||
* class-based proxy mechanisms.
|
||||
* @param method the handler method
|
||||
* @param targetBean the bean instance
|
||||
* @param args the method arguments
|
||||
*/
|
||||
protected void assertTargetBean(Method method, Object targetBean, Object[] args) {
|
||||
Class<?> methodDeclaringClass = method.getDeclaringClass();
|
||||
@@ -347,9 +360,9 @@ public class HandlerMethod {
|
||||
protected String formatInvokeError(String text, Object[] args) {
|
||||
|
||||
String formattedArgs = IntStream.range(0, args.length)
|
||||
.mapToObj(i -> (args[i] != null ?
|
||||
.mapToObj((i) -> (args[i] != null) ?
|
||||
"[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" :
|
||||
"[" + i + "] [null]"))
|
||||
"[" + i + "] [null]")
|
||||
.collect(Collectors.joining(",\n", " ", " "));
|
||||
|
||||
return text + "\n" +
|
||||
@@ -401,21 +414,7 @@ public class HandlerMethod {
|
||||
if (index < ifcAnns.length) {
|
||||
Annotation[] paramAnns = ifcAnns[index];
|
||||
if (paramAnns.length > 0) {
|
||||
List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length);
|
||||
merged.addAll(Arrays.asList(anns));
|
||||
for (Annotation paramAnn : paramAnns) {
|
||||
boolean existingType = false;
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.annotationType() == paramAnn.annotationType()) {
|
||||
existingType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!existingType) {
|
||||
merged.add(adaptAnnotation(paramAnn));
|
||||
}
|
||||
}
|
||||
anns = merged.toArray(new Annotation[0]);
|
||||
anns = mergeAnnotations(anns, paramAnns);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,6 +423,25 @@ public class HandlerMethod {
|
||||
}
|
||||
return anns;
|
||||
}
|
||||
|
||||
private Annotation[] mergeAnnotations(Annotation[] anns, Annotation[] paramAnns) {
|
||||
List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length);
|
||||
merged.addAll(Arrays.asList(anns));
|
||||
for (Annotation paramAnn : paramAnns) {
|
||||
boolean existingType = false;
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.annotationType() == paramAnn.annotationType()) {
|
||||
existingType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!existingType) {
|
||||
merged.add(adaptAnnotation(paramAnn));
|
||||
}
|
||||
}
|
||||
anns = merged.toArray(new Annotation[0]);
|
||||
return anns;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -435,7 +453,7 @@ public class HandlerMethod {
|
||||
@Nullable
|
||||
private final Object returnValue;
|
||||
|
||||
public ReturnValueMethodParameter(@Nullable Object returnValue) {
|
||||
ReturnValueMethodParameter(@Nullable Object returnValue) {
|
||||
super(-1);
|
||||
this.returnValue = returnValue;
|
||||
}
|
||||
@@ -447,7 +465,7 @@ public class HandlerMethod {
|
||||
|
||||
@Override
|
||||
public Class<?> getParameterType() {
|
||||
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
|
||||
return (this.returnValue != null) ? this.returnValue.getClass() : super.getParameterType();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
@@ -35,20 +36,18 @@ public interface HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* Whether this resolver supports the given {@link MethodParameter}.
|
||||
* @param parameter the method parameter to check for support
|
||||
*/
|
||||
boolean supportsParameter(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Resolve a method parameter to a value.
|
||||
*
|
||||
* @param parameter the method parameter to resolve. This parameter must
|
||||
* have previously checked via {@link #supportsParameter}.
|
||||
* @param environment the environment to use to resolve the value
|
||||
*
|
||||
* @return the resolved value, which may be {@code null} if not resolved;
|
||||
* the value may also be a {@link reactor.core.publisher.Mono} if it
|
||||
* requires asynchronous resolution.
|
||||
*
|
||||
* @throws Exception in case of errors with the preparation of argument values
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -43,6 +44,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
|
||||
/**
|
||||
* Add the given {@link HandlerMethodArgumentResolver}.
|
||||
* @param resolver the argument resolver
|
||||
*/
|
||||
public void addResolver(HandlerMethodArgumentResolver resolver) {
|
||||
this.argumentResolvers.add(resolver);
|
||||
@@ -84,10 +86,11 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodArgumentResolver} that supports
|
||||
* the given method parameter.
|
||||
* @param parameter the method parameter
|
||||
*/
|
||||
@Nullable
|
||||
public HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
|
||||
return this.argumentResolverCache.computeIfAbsent(parameter, p -> {
|
||||
return this.argumentResolverCache.computeIfAbsent(parameter, (p) -> {
|
||||
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
|
||||
if (resolver.supportsParameter(parameter)) {
|
||||
return resolver;
|
||||
@@ -97,4 +100,4 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -69,6 +70,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
|
||||
|
||||
/**
|
||||
* Invoke the handler method with the given argument values.
|
||||
* @param graphQLContext the GraphQL context for this data fetching operation
|
||||
* @param argValues the values to use to invoke the method
|
||||
* @return the value returned from the method or a {@code Mono<Throwable>}
|
||||
* if the invocation fails.
|
||||
@@ -89,7 +91,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
assertTargetBean(method, getBean(), argValues);
|
||||
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
|
||||
String text = (ex.getMessage() != null) ? ex.getMessage() : "Illegal argument";
|
||||
return Mono.error(new IllegalStateException(formatInvokeError(text, argValues), ex));
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
@@ -143,15 +145,16 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
|
||||
/**
|
||||
* Use this method to resolve the arguments asynchronously. This is only
|
||||
* useful when at least one of the values is a {@link Mono}
|
||||
* @param args the arguments to be resolved asynchronously
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Mono<Object[]> toArgsMono(Object[] args) {
|
||||
List<Mono<Object>> monoList = new ArrayList<>();
|
||||
for (Object arg : args) {
|
||||
Mono<Object> argMono = (arg instanceof Mono ? (Mono<Object>) arg : Mono.justOrEmpty(arg));
|
||||
Mono<Object> argMono = ((arg instanceof Mono) ? (Mono<Object>) arg : Mono.justOrEmpty(arg));
|
||||
monoList.add(argMono.defaultIfEmpty(NO_VALUE));
|
||||
}
|
||||
return Mono.zip(monoList, values -> {
|
||||
return Mono.zip(monoList, (values) -> {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i] == NO_VALUE) {
|
||||
values[i] = null;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -103,8 +104,6 @@ import org.springframework.validation.DataBinder;
|
||||
* is configured for use.
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
@@ -113,17 +112,17 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
|
||||
private static final ClassLoader classLoader = AnnotatedControllerConfigurer.class.getClassLoader();
|
||||
|
||||
private final static boolean springDataPresent = ClassUtils.isPresent(
|
||||
private static final boolean springDataPresent = ClassUtils.isPresent(
|
||||
"org.springframework.data.projection.SpelAwareProxyProjectionFactory", classLoader);
|
||||
|
||||
private final static boolean springSecurityPresent = ClassUtils.isPresent(
|
||||
private static final boolean springSecurityPresent = ClassUtils.isPresent(
|
||||
"org.springframework.security.core.context.SecurityContext", classLoader);
|
||||
|
||||
private final static boolean beanValidationPresent = ClassUtils.isPresent(
|
||||
private static final boolean beanValidationPresent = ClassUtils.isPresent(
|
||||
"jakarta.validation.executable.ExecutableValidator", classLoader);
|
||||
|
||||
|
||||
private final static Log logger = LogFactory.getLog(AnnotatedControllerConfigurer.class);
|
||||
private static final Log logger = LogFactory.getLog(AnnotatedControllerConfigurer.class);
|
||||
|
||||
/**
|
||||
* Bean name prefix for target beans behind scoped proxies. Used to exclude those
|
||||
@@ -165,6 +164,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
* that assists in binding GraphQL arguments onto
|
||||
* {@link org.springframework.graphql.data.method.annotation.Argument @Argument}
|
||||
* annotated method parameters.
|
||||
* @param registrar the formatter registrar
|
||||
*/
|
||||
public void addFormatterRegistrar(FormatterRegistrar registrar) {
|
||||
registrar.registerFormatters(this.conversionService);
|
||||
@@ -175,6 +175,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
* {@link org.springframework.graphql.data.method.annotation.Argument @Argument}
|
||||
* should falls back to direct field access in case the target object does
|
||||
* not use accessor methods.
|
||||
* @param fallBackOnDirectFieldAccess whether we should fall back on direct field access
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public void setFallBackOnDirectFieldAccess(boolean fallBackOnDirectFieldAccess) {
|
||||
@@ -185,7 +186,6 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
* Add a {@link HandlerMethodArgumentResolver} for custom controller method
|
||||
* arguments. Such custom resolvers are ordered after built-in resolvers
|
||||
* except for {@link SourceMethodArgumentResolver}, which is always last.
|
||||
*
|
||||
* @param resolver the resolver to add.
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@@ -206,11 +206,9 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
* exceptions from non-controller {@link DataFetcher}s since exceptions from
|
||||
* {@code @SchemaMapping} controller methods are handled automatically at
|
||||
* the point of invocation.
|
||||
*
|
||||
* @return a resolver instance that can be plugged into
|
||||
* {@link org.springframework.graphql.execution.GraphQlSource.Builder#exceptionResolvers(List)
|
||||
* GraphQlSource.Builder}
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public DataFetcherExceptionResolver getExceptionResolver() {
|
||||
@@ -352,7 +350,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
else {
|
||||
dataFetcher = registerBatchLoader(info);
|
||||
}
|
||||
runtimeWiringBuilder.type(info.getCoordinates().getTypeName(), typeBuilder ->
|
||||
runtimeWiringBuilder.type(info.getCoordinates().getTypeName(), (typeBuilder) ->
|
||||
typeBuilder.dataFetcher(info.getCoordinates().getFieldName(), dataFetcher));
|
||||
});
|
||||
}
|
||||
@@ -482,17 +480,17 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
|
||||
private HandlerMethod createHandlerMethod(Method method, Object handler, Class<?> handlerType) {
|
||||
Method theMethod = AopUtils.selectInvocableMethod(method, handlerType);
|
||||
return (handler instanceof String ?
|
||||
return (handler instanceof String) ?
|
||||
new HandlerMethod((String) handler, obtainApplicationContext().getAutowireCapableBeanFactory(), theMethod) :
|
||||
new HandlerMethod(handler, theMethod));
|
||||
new HandlerMethod(handler, theMethod);
|
||||
}
|
||||
|
||||
private String formatMappings(Class<?> handlerType, Collection<MappingInfo> infos) {
|
||||
String formattedType = Arrays.stream(ClassUtils.getPackageName(handlerType).split("\\."))
|
||||
.map(p -> p.substring(0, 1))
|
||||
.map((p) -> p.substring(0, 1))
|
||||
.collect(Collectors.joining(".", "", "." + handlerType.getSimpleName()));
|
||||
return infos.stream()
|
||||
.map(mappingInfo -> {
|
||||
.map((mappingInfo) -> {
|
||||
Method method = mappingInfo.getHandlerMethod().getMethod();
|
||||
String methodParameters = Arrays.stream(method.getGenericParameterTypes())
|
||||
.map(Type::getTypeName)
|
||||
@@ -511,7 +509,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
BatchLoaderRegistry registry = obtainApplicationContext().getBean(BatchLoaderRegistry.class);
|
||||
BatchLoaderRegistry.RegistrationSpec<Object, Object> registration = registry.forName(dataLoaderKey);
|
||||
if (info.getMaxBatchSize() > 0) {
|
||||
registration.withOptions(options -> options.setMaxBatchSize(info.getMaxBatchSize()));
|
||||
registration.withOptions((options) -> options.setMaxBatchSize(info.getMaxBatchSize()));
|
||||
}
|
||||
|
||||
HandlerMethod handlerMethod = info.getHandlerMethod();
|
||||
@@ -551,6 +549,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
* Alternative to {@link #configure(RuntimeWiring.Builder)} that registers
|
||||
* data fetchers in a {@link GraphQLCodeRegistry.Builder}. This could be
|
||||
* used with programmatic creation of {@link graphql.schema.GraphQLSchema}.
|
||||
* @param codeRegistryBuilder the code registry to be processed
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void configure(GraphQLCodeRegistry.Builder codeRegistryBuilder) {
|
||||
@@ -577,7 +576,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
public MappingInfo(
|
||||
MappingInfo(
|
||||
String typeName, String field, boolean batchMapping, int maxBatchSize,
|
||||
HandlerMethod handlerMethod) {
|
||||
|
||||
@@ -587,20 +586,20 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
this.handlerMethod = handlerMethod;
|
||||
}
|
||||
|
||||
public FieldCoordinates getCoordinates() {
|
||||
FieldCoordinates getCoordinates() {
|
||||
return this.coordinates;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isBatchMapping() {
|
||||
boolean isBatchMapping() {
|
||||
return this.batchMapping;
|
||||
}
|
||||
|
||||
public int getMaxBatchSize() {
|
||||
int getMaxBatchSize() {
|
||||
return this.maxBatchSize;
|
||||
}
|
||||
|
||||
public HandlerMethod getHandlerMethod() {
|
||||
HandlerMethod getHandlerMethod() {
|
||||
return this.handlerMethod;
|
||||
}
|
||||
|
||||
@@ -639,7 +638,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
this.argumentResolvers = argumentResolvers;
|
||||
|
||||
this.methodValidationHelper =
|
||||
(helper != null ? helper.getValidationHelperFor(info.getHandlerMethod()) : null);
|
||||
(helper != null) ? helper.getValidationHelperFor(info.getHandlerMethod()) : null;
|
||||
|
||||
// Register controllers early to validate exception handler return types
|
||||
Class<?> controllerType = info.getHandlerMethod().getBeanType();
|
||||
@@ -664,7 +663,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
/**
|
||||
* Return the {@link HandlerMethod} used to fetch data.
|
||||
*/
|
||||
public HandlerMethod getHandlerMethod() {
|
||||
HandlerMethod getHandlerMethod() {
|
||||
return this.info.getHandlerMethod();
|
||||
}
|
||||
|
||||
@@ -690,13 +689,13 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod, Object result) {
|
||||
|
||||
if (this.subscription && result instanceof Publisher<?> publisher) {
|
||||
result = Flux.from(publisher).onErrorResume(ex -> handleSubscriptionError(ex, env, handlerMethod));
|
||||
result = Flux.from(publisher).onErrorResume((ex) -> handleSubscriptionError(ex, env, handlerMethod));
|
||||
}
|
||||
else if (result instanceof Mono) {
|
||||
result = ((Mono<T>) result).onErrorResume(ex -> (Mono<T>) handleException(ex, env, handlerMethod));
|
||||
result = ((Mono<T>) result).onErrorResume((ex) -> (Mono<T>) handleException(ex, env, handlerMethod));
|
||||
}
|
||||
else if (result instanceof Flux<?>) {
|
||||
result = ((Flux<T>) result).onErrorResume(ex -> (Mono<T>) handleException(ex, env, handlerMethod));
|
||||
result = ((Flux<T>) result).onErrorResume((ex) -> (Mono<T>) handleException(ex, env, handlerMethod));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -705,7 +704,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
Throwable ex, DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod) {
|
||||
|
||||
return this.exceptionResolver.resolveException(ex, env, handlerMethod.getBean())
|
||||
.map(errors -> DataFetcherResult.newResult().errors(errors).build())
|
||||
.map((errors) -> DataFetcherResult.newResult().errors(errors).build())
|
||||
.switchIfEmpty(Mono.error(ex));
|
||||
}
|
||||
|
||||
@@ -714,7 +713,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I
|
||||
Throwable ex, DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod) {
|
||||
|
||||
return (Publisher<T>) this.exceptionResolver.resolveException(ex, env, handlerMethod.getBean())
|
||||
.flatMap(errors -> Mono.error(new SubscriptionPublisherException(errors, ex)))
|
||||
.flatMap((errors) -> Mono.error(new SubscriptionPublisherException(errors, ex)))
|
||||
.switchIfEmpty(Mono.error(ex));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -72,7 +73,6 @@ import org.springframework.web.method.ControllerAdviceBean;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 1.2.0
|
||||
*/
|
||||
final class AnnotatedControllerExceptionResolver {
|
||||
|
||||
@@ -98,9 +98,9 @@ final class AnnotatedControllerExceptionResolver {
|
||||
* are validated to ensure they are within a range of supported types.
|
||||
* @param controllerType the controller type to register
|
||||
*/
|
||||
public void registerController(Class<?> controllerType) {
|
||||
void registerController(Class<?> controllerType) {
|
||||
this.controllerCache.computeIfAbsent(
|
||||
controllerType, type -> new MethodResolver(findExceptionHandlers(controllerType)));
|
||||
controllerType, (type) -> new MethodResolver(findExceptionHandlers(controllerType)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +110,7 @@ final class AnnotatedControllerExceptionResolver {
|
||||
* for use at runtime.
|
||||
* @param context the context to look into
|
||||
*/
|
||||
public void registerControllerAdvice(ApplicationContext context) {
|
||||
void registerControllerAdvice(ApplicationContext context) {
|
||||
Map<ControllerAdviceBean, MethodResolver> detectedControllerAdvice = new HashMap<>();
|
||||
for (ControllerAdviceBean bean : ControllerAdviceBean.findAnnotatedBeans(context)) {
|
||||
Class<?> beanType = bean.getBeanType();
|
||||
@@ -121,12 +121,11 @@ final class AnnotatedControllerExceptionResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
detectedControllerAdvice.keySet().stream().sorted(OrderComparator.INSTANCE).forEach(bean -> {
|
||||
this.controllerAdviceCache.put(bean, detectedControllerAdvice.get(bean));
|
||||
});
|
||||
detectedControllerAdvice.keySet().stream().sorted(OrderComparator.INSTANCE).forEach((bean) ->
|
||||
this.controllerAdviceCache.put(bean, detectedControllerAdvice.get(bean)));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("@GraphQlException methods in ControllerAdvice beans: " +
|
||||
(this.controllerAdviceCache.size() == 0 ? "none" : this.controllerAdviceCache.size()));
|
||||
((this.controllerAdviceCache.size() == 0) ? "none" : this.controllerAdviceCache.size()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +133,7 @@ final class AnnotatedControllerExceptionResolver {
|
||||
private static Map<Class<? extends Throwable>, Method> findExceptionHandlers(Class<?> handlerType) {
|
||||
|
||||
Map<Method, GraphQlExceptionHandler> handlerMap = MethodIntrospector.selectMethods(
|
||||
handlerType, (MethodIntrospector.MetadataLookup<GraphQlExceptionHandler>) method ->
|
||||
handlerType, (MethodIntrospector.MetadataLookup<GraphQlExceptionHandler>) (method) ->
|
||||
AnnotatedElementUtils.findMergedAnnotation(method, GraphQlExceptionHandler.class));
|
||||
|
||||
Map<Class<? extends Throwable>, Method> mappings = new HashMap<>(handlerMap.size());
|
||||
@@ -172,7 +171,7 @@ final class AnnotatedControllerExceptionResolver {
|
||||
* @return a {@code Mono} with resolved {@code GraphQLError}s as specified in
|
||||
* {@link DataFetcherExceptionResolver#resolveException(Throwable, DataFetchingEnvironment)}
|
||||
*/
|
||||
public Mono<List<GraphQLError>> resolveException(
|
||||
Mono<List<GraphQLError>> resolveException(
|
||||
Throwable ex, DataFetchingEnvironment environment, @Nullable Object controller) {
|
||||
|
||||
Object controllerOrAdvice = null;
|
||||
@@ -229,7 +228,7 @@ final class AnnotatedControllerExceptionResolver {
|
||||
while (exToExpose != null) {
|
||||
exceptions.add(exToExpose);
|
||||
Throwable cause = exToExpose.getCause();
|
||||
exToExpose = (cause != exToExpose ? cause : null);
|
||||
exToExpose = (cause != exToExpose) ? cause : null;
|
||||
}
|
||||
Object[] arguments = new Object[exceptions.size() + 1];
|
||||
exceptions.toArray(arguments); // efficient arraycopy call in ArrayList
|
||||
@@ -277,7 +276,7 @@ final class AnnotatedControllerExceptionResolver {
|
||||
* @return the exception handler to use, or {@code null} if no match
|
||||
*/
|
||||
@Nullable
|
||||
public MethodHolder resolveMethod(Throwable exception) {
|
||||
MethodHolder resolveMethod(Throwable exception) {
|
||||
MethodHolder method = resolveMethodByExceptionType(exception.getClass());
|
||||
if (method == null) {
|
||||
Throwable cause = exception.getCause();
|
||||
@@ -295,7 +294,7 @@ final class AnnotatedControllerExceptionResolver {
|
||||
method = getMappedMethod(exceptionType);
|
||||
this.resolvedExceptionCache.put(exceptionType, method);
|
||||
}
|
||||
return (method != NO_MATCH ? method : null);
|
||||
return (method != NO_MATCH) ? method : null;
|
||||
}
|
||||
|
||||
private MethodHolder getMappedMethod(Class<? extends Throwable> exceptionType) {
|
||||
@@ -341,11 +340,11 @@ final class AnnotatedControllerExceptionResolver {
|
||||
this.adapter = ReturnValueAdapter.createFor(this.returnType);
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public Mono<List<GraphQLError>> adapt(@Nullable Object result, Throwable ex) {
|
||||
Mono<List<GraphQLError>> adapt(@Nullable Object result, Throwable ex) {
|
||||
return this.adapter.adapt(result, this.returnType, ex);
|
||||
}
|
||||
|
||||
@@ -420,24 +419,24 @@ final class AnnotatedControllerExceptionResolver {
|
||||
}
|
||||
|
||||
|
||||
/** Adapter for void */
|
||||
/* Adapter for void */
|
||||
ReturnValueAdapter forVoid = (result, returnType, ex) -> Mono.just(Collections.emptyList());
|
||||
|
||||
/** Adapter for a single GraphQLError */
|
||||
/* Adapter for a single GraphQLError */
|
||||
ReturnValueAdapter forSingleError = (result, returnType, ex) ->
|
||||
(result == null ?
|
||||
Mono.empty() :
|
||||
Mono.just(Collections.singletonList((GraphQLError) result)));
|
||||
(result != null) ?
|
||||
Mono.just(Collections.singletonList((GraphQLError) result)) :
|
||||
Mono.empty();
|
||||
|
||||
/** Adapter for a collection of GraphQLError's */
|
||||
/* Adapter for a collection of GraphQLError's */
|
||||
ReturnValueAdapter forCollection = (result, returnType, ex) ->
|
||||
(result == null ?
|
||||
Mono.empty() :
|
||||
Mono.just((result instanceof List ?
|
||||
(result != null) ?
|
||||
Mono.just((result instanceof List) ?
|
||||
(List<GraphQLError>) result :
|
||||
new ArrayList<>((Collection<GraphQLError>) result))));
|
||||
new ArrayList<>((Collection<GraphQLError>) result)) :
|
||||
Mono.empty();
|
||||
|
||||
/** Adapter for Object */
|
||||
/* Adapter for Object */
|
||||
ReturnValueAdapter forObject = (result, returnType, ex) -> {
|
||||
if (result == null) {
|
||||
return Mono.empty();
|
||||
@@ -457,15 +456,15 @@ final class AnnotatedControllerExceptionResolver {
|
||||
}
|
||||
};
|
||||
|
||||
/** Adapter for {@code Mono<Void>} */
|
||||
/* Adapter for {@code Mono<Void>} */
|
||||
ReturnValueAdapter forMonoVoid = (result, returnType, ex) ->
|
||||
(result == null ? Mono.empty() : Mono.just(Collections.emptyList()));
|
||||
(result != null) ? Mono.just(Collections.emptyList()) : Mono.empty();
|
||||
|
||||
/** Adapter for a {@code Mono} wrapping any of the other synchronous return value types */
|
||||
/* Adapter for a {@code Mono} wrapping any of the other synchronous return value types */
|
||||
ReturnValueAdapter forMono = (result, returnType, ex) ->
|
||||
(result == null ?
|
||||
Mono.empty() :
|
||||
((Mono<?>) result).flatMap(o -> forObject.adapt(o, returnType, ex)).switchIfEmpty(Mono.error(ex)));
|
||||
(result != null) ?
|
||||
((Mono<?>) result).flatMap((o) -> forObject.adapt(o, returnType, ex)).switchIfEmpty(Mono.error(ex)) :
|
||||
Mono.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
@@ -19,22 +19,24 @@ package org.springframework.graphql.data.method.annotation.support;
|
||||
import jakarta.validation.valueextraction.ExtractedValue;
|
||||
import jakarta.validation.valueextraction.UnwrapByDefault;
|
||||
import jakarta.validation.valueextraction.ValueExtractor;
|
||||
|
||||
import org.springframework.graphql.data.ArgumentValue;
|
||||
|
||||
/**
|
||||
* {@link ValueExtractor} that enables {@code @Valid} with {@link ArgumentValue},
|
||||
* and helps to extract the value from it.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.2
|
||||
*/
|
||||
@UnwrapByDefault
|
||||
public final class ArgumentValueValueExtractor implements ValueExtractor<ArgumentValue<@ExtractedValue ?>> {
|
||||
|
||||
@Override
|
||||
public void extractValues(ArgumentValue<?> argumentValue, ValueReceiver receiver) {
|
||||
if (!argumentValue.isOmitted()) {
|
||||
receiver.value(null, argumentValue.value());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void extractValues(ArgumentValue<?> argumentValue, ValueReceiver receiver) {
|
||||
if (!argumentValue.isOmitted()) {
|
||||
receiver.value(null, argumentValue.value());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -97,7 +98,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
|
||||
return getCurrentAuthentication(parameter)
|
||||
.mapNotNull(auth -> resolvePrincipal(parameter, auth.getPrincipal()))
|
||||
.mapNotNull((auth) -> resolvePrincipal(parameter, auth.getPrincipal()))
|
||||
.transform((argument) -> isPublisherOrMono(parameter) ? Mono.just(argument) : argument);
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
|
||||
@SuppressWarnings("unchecked")
|
||||
private Mono<Authentication> getCurrentAuthentication(MethodParameter parameter) {
|
||||
Object value = PrincipalMethodArgumentResolver.resolveAuthentication(parameter);
|
||||
return (value instanceof Authentication auth ? Mono.just(auth) : (Mono<Authentication>) value);
|
||||
return (value instanceof Authentication auth) ? Mono.just(auth) : (Mono<Authentication>) value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.security.Principal;
|
||||
@@ -50,7 +51,7 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
|
||||
private final static boolean springSecurityPresent = ClassUtils.isPresent(
|
||||
private static final boolean springSecurityPresent = ClassUtils.isPresent(
|
||||
"org.springframework.security.core.context.SecurityContext",
|
||||
AnnotatedControllerConfigurer.class.getClassLoader());
|
||||
|
||||
@@ -66,7 +67,6 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
/**
|
||||
* Invoke the underlying batch loader method with a collection of keys to
|
||||
* return a Map of key-value pairs.
|
||||
*
|
||||
* @param keys the keys for which to load values
|
||||
* @param environment the environment available to batch loaders
|
||||
* @param <K> the type of keys in the map
|
||||
@@ -80,7 +80,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
Object result = doInvoke(environment.getContext(), args);
|
||||
return toMonoMap(result);
|
||||
}
|
||||
return toArgsMono(args).flatMap(argValues -> {
|
||||
return toArgsMono(args).flatMap((argValues) -> {
|
||||
Object result = doInvoke(environment.getContext(), argValues);
|
||||
return toMonoMap(result);
|
||||
});
|
||||
@@ -89,7 +89,6 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
/**
|
||||
* Invoke the underlying batch loader method with a collection of input keys
|
||||
* to return a collection of matching values.
|
||||
*
|
||||
* @param keys the keys for which to load values
|
||||
* @param environment the environment available to batch loaders
|
||||
* @param <V> the type of values returned
|
||||
@@ -101,7 +100,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
Object result = doInvoke(environment.getContext(), args);
|
||||
return toFlux(result);
|
||||
}
|
||||
return toArgsMono(args).flatMapMany(resolvedArgs -> {
|
||||
return toArgsMono(args).flatMapMany((resolvedArgs) -> {
|
||||
Object result = doInvoke(environment.getContext(), resolvedArgs);
|
||||
return toFlux(result);
|
||||
});
|
||||
@@ -164,7 +163,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
}
|
||||
|
||||
private boolean doesNotHaveAsyncArgs(Object[] args) {
|
||||
return Arrays.stream(args).noneMatch(arg -> arg instanceof Mono);
|
||||
return Arrays.stream(args).noneMatch((arg) -> arg instanceof Mono);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -176,7 +175,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
return (Mono<Map<K, V>>) result;
|
||||
}
|
||||
else if (result instanceof CompletableFuture) {
|
||||
return Mono.fromFuture((CompletableFuture<? extends Map<K,V>>) result);
|
||||
return Mono.fromFuture((CompletableFuture<? extends Map<K, V>>) result);
|
||||
}
|
||||
return Mono.error(new IllegalStateException("Unexpected return value: " + result));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -72,7 +73,7 @@ public class ContextValueMethodArgumentResolver implements HandlerMethodArgument
|
||||
@Nullable GraphQLContext graphQlContext) {
|
||||
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
Object value = (graphQlContext != null ? graphQlContext.get(contextValueName) : null);
|
||||
Object value = (graphQlContext != null) ? graphQlContext.get(contextValueName) : null;
|
||||
|
||||
boolean isOptional = parameterType.equals(Optional.class);
|
||||
boolean isMono = parameterType.equals(Mono.class);
|
||||
@@ -85,14 +86,14 @@ public class ContextValueMethodArgumentResolver implements HandlerMethodArgument
|
||||
if (value == null) {
|
||||
value = Mono.empty();
|
||||
}
|
||||
else if (!( value instanceof Mono)) {
|
||||
else if (!(value instanceof Mono)) {
|
||||
value = Mono.just(value);
|
||||
}
|
||||
return Mono.just(value);
|
||||
}
|
||||
|
||||
if (isOptional) {
|
||||
return (value instanceof Optional ? value : Optional.ofNullable(value));
|
||||
return (value instanceof Optional) ? value : Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
|
||||
@@ -27,14 +29,14 @@ import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
*/
|
||||
public class ContinuationHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return "kotlin.coroutines.Continuation".equals(parameter.getParameterType().getName());
|
||||
}
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return "kotlin.coroutines.Continuation".equals(parameter.getParameterType().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -52,7 +54,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
private final HandlerMethodArgumentResolverComposite resolvers;
|
||||
|
||||
private final BiConsumer<Object, Object[]> validationHelper;
|
||||
|
||||
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
private final boolean subscription;
|
||||
@@ -63,6 +65,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
* @param handlerMethod the handler method
|
||||
* @param resolvers the argument resolvers
|
||||
* @param validationHelper to apply bean validation with
|
||||
* @param executor an {@link Executor} to use for {@link Callable} return values
|
||||
* @param subscription whether the field being fetched is of subscription type
|
||||
*/
|
||||
public DataFetcherHandlerMethod(
|
||||
@@ -73,7 +76,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
super(handlerMethod, executor);
|
||||
Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers");
|
||||
this.resolvers = resolvers;
|
||||
this.validationHelper = (validationHelper != null ? validationHelper : (controller, args) -> {});
|
||||
this.validationHelper = (validationHelper != null) ? validationHelper : (controller, args) -> { };
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
@@ -95,9 +98,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
* The {@code providedArgs} parameter however may supply argument values to
|
||||
* be used directly, i.e. without argument resolution. Provided argument
|
||||
* values are checked before argument resolvers.
|
||||
*
|
||||
* @param environment the environment to resolve arguments from
|
||||
*
|
||||
* @return the raw value returned by the invoked method, possibly a
|
||||
* {@code Mono} in case a method argument requires asynchronous resolution;
|
||||
* {@code Mono<Throwable>} is returned if invocation fails.
|
||||
@@ -110,6 +111,8 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
/**
|
||||
* Variant of {@link #invoke(DataFetchingEnvironment)} that also accepts
|
||||
* "given" arguments, which are matched by type.
|
||||
* @param environment the data fetching environment for this operation
|
||||
* @param providedArgs the argument values to be used directly
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Nullable
|
||||
@@ -122,17 +125,17 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
|
||||
if (Arrays.stream(args).noneMatch(arg -> arg instanceof Mono)) {
|
||||
if (Arrays.stream(args).noneMatch((arg) -> arg instanceof Mono)) {
|
||||
return validateAndInvoke(args, environment);
|
||||
}
|
||||
|
||||
return this.subscription ?
|
||||
toArgsMono(args).flatMapMany(argValues -> {
|
||||
toArgsMono(args).flatMapMany((argValues) -> {
|
||||
Object result = validateAndInvoke(argValues, environment);
|
||||
Assert.state(result instanceof Publisher, "Expected a Publisher from a Subscription response");
|
||||
return Flux.from((Publisher<?>) result);
|
||||
}) :
|
||||
toArgsMono(args).flatMap(argValues -> {
|
||||
toArgsMono(args).flatMap((argValues) -> {
|
||||
Object result = validateAndInvoke(argValues, environment);
|
||||
if (result instanceof Mono<?> mono) {
|
||||
return mono;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.util.Locale;
|
||||
@@ -27,7 +28,7 @@ import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Resolver for {@link DataFetchingEnvironment} and related values that can be
|
||||
* accessed through the {@link DataFetchingEnvironment} such as:
|
||||
* accessed through the {@link DataFetchingEnvironment}. This includes:
|
||||
* <ul>
|
||||
* <li>{@link GraphQLContext}
|
||||
* <li>{@link DataFetchingFieldSelectionSet}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -41,7 +42,7 @@ import org.springframework.util.Assert;
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DataLoaderMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.getParameterType().equals(DataLoader.class);
|
||||
@@ -80,8 +81,8 @@ public class DataLoaderMethodArgumentResolver implements HandlerMethodArgumentRe
|
||||
ParameterizedType parameterizedType = (ParameterizedType) genericType;
|
||||
if (parameterizedType.getActualTypeArguments().length == 2) {
|
||||
Type valueType = parameterizedType.getActualTypeArguments()[1];
|
||||
return (valueType instanceof Class ?
|
||||
(Class<?>) valueType : ResolvableType.forType(valueType).resolve());
|
||||
return (valueType instanceof Class) ?
|
||||
(Class<?>) valueType : ResolvableType.forType(valueType).resolve();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -92,7 +93,7 @@ public class DataLoaderMethodArgumentResolver implements HandlerMethodArgumentRe
|
||||
@Nullable Class<?> valueType, @Nullable String parameterName) {
|
||||
|
||||
String message = "Cannot resolve DataLoader for parameter" +
|
||||
(parameterName != null ? " '" + parameterName + "'" : "[" + parameter.getParameterIndex() + "]" ) +
|
||||
((parameterName != null) ? " '" + parameterName + "'" : "[" + parameter.getParameterIndex() + "]") +
|
||||
" in method " + parameter.getMethod().toGenericString() + ". ";
|
||||
|
||||
if (valueType == null) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import graphql.GraphQLContext;
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
@@ -26,7 +28,6 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Resolver to obtain {@link Principal} from Spring Security context via
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2020-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
|
||||
@@ -75,7 +76,7 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu
|
||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||
this.projectionFactory.setBeanFactory(applicationContext);
|
||||
ClassLoader classLoader = applicationContext.getClassLoader();
|
||||
if(classLoader != null) {
|
||||
if (classLoader != null) {
|
||||
this.projectionFactory.setBeanClassLoader(classLoader);
|
||||
}
|
||||
}
|
||||
@@ -98,8 +99,8 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu
|
||||
|
||||
private static Class<?> getTargetType(MethodParameter parameter) {
|
||||
Class<?> type = parameter.getParameterType();
|
||||
return (type.equals(Optional.class) || type.equals(ArgumentValue.class) ?
|
||||
parameter.nested().getNestedParameterType() : parameter.getParameterType());
|
||||
return (type.equals(Optional.class) || type.equals(ArgumentValue.class)) ?
|
||||
parameter.nested().getNestedParameterType() : parameter.getParameterType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,15 +118,15 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu
|
||||
}
|
||||
|
||||
Map<String, Object> arguments = environment.getArguments();
|
||||
Object rawValue = (name != null ? arguments.get(name) : arguments);
|
||||
Object value = (rawValue != null ? createProjection(targetType, rawValue) : null);
|
||||
Object rawValue = (name != null) ? arguments.get(name) : arguments;
|
||||
Object value = (rawValue != null) ? createProjection(targetType, rawValue) : null;
|
||||
|
||||
if (isOptional) {
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
else if (isArgumentValue) {
|
||||
return (name != null && arguments.containsKey(name) ?
|
||||
ArgumentValue.ofNullable(value) : ArgumentValue.omitted());
|
||||
return (name != null && arguments.containsKey(name)) ?
|
||||
ArgumentValue.ofNullable(value) : ArgumentValue.omitted();
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
@@ -140,7 +141,7 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu
|
||||
* or the map of arguments
|
||||
* @return the created project instance
|
||||
*/
|
||||
protected Object createProjection(Class<?> targetType, Object rawValue){
|
||||
protected Object createProjection(Class<?> targetType, Object rawValue) {
|
||||
return this.projectionFactory.createProjection(targetType, rawValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,6 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
|
||||
import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryInitializationAotProcessor} implementation for registering
|
||||
* runtime hints discoverable through GraphQL controllers, such as:
|
||||
@@ -80,11 +78,10 @@ import static org.springframework.core.annotation.MergedAnnotations.SearchStrate
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @see org.springframework.graphql.data.method.HandlerMethodArgumentResolver
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
|
||||
|
||||
private final static boolean springDataPresent = ClassUtils.isPresent(
|
||||
private static final boolean springDataPresent = ClassUtils.isPresent(
|
||||
"org.springframework.data.projection.SpelAwareProxyProjectionFactory",
|
||||
SchemaMappingBeanFactoryInitializationAotProcessor.class.getClassLoader());
|
||||
|
||||
@@ -94,8 +91,8 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
List<Class<?>> controllers = new ArrayList<>();
|
||||
List<Class<?>> controllerAdvices = new ArrayList<>();
|
||||
Arrays.stream(beanFactory.getBeanDefinitionNames())
|
||||
.map(beanName -> RegisteredBean.of(beanFactory, beanName).getBeanClass())
|
||||
.forEach(beanClass -> {
|
||||
.map((beanName) -> RegisteredBean.of(beanFactory, beanName).getBeanClass())
|
||||
.forEach((beanClass) -> {
|
||||
if (isController(beanClass)) {
|
||||
controllers.add(beanClass);
|
||||
}
|
||||
@@ -107,11 +104,11 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
}
|
||||
|
||||
private boolean isController(AnnotatedElement element) {
|
||||
return MergedAnnotations.from(element, TYPE_HIERARCHY).isPresent(Controller.class);
|
||||
return MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).isPresent(Controller.class);
|
||||
}
|
||||
|
||||
private boolean isControllerAdvice(AnnotatedElement element) {
|
||||
return MergedAnnotations.from(element, TYPE_HIERARCHY).isPresent(ControllerAdvice.class);
|
||||
return MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).isPresent(ControllerAdvice.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +121,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
|
||||
private final HandlerMethodArgumentResolverComposite argumentResolvers;
|
||||
|
||||
public SchemaMappingBeanFactoryInitializationAotContribution(List<Class<?>> controllers, List<Class<?>> controllerAdvices) {
|
||||
SchemaMappingBeanFactoryInitializationAotContribution(List<Class<?>> controllers, List<Class<?>> controllerAdvices) {
|
||||
this.controllers = controllers;
|
||||
this.controllerAdvices = controllerAdvices;
|
||||
this.argumentResolvers = createArgumentResolvers();
|
||||
@@ -141,19 +138,19 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
public void applyTo(GenerationContext context, BeanFactoryInitializationCode initializationCode) {
|
||||
RuntimeHints runtimeHints = context.getRuntimeHints();
|
||||
registerSpringDataSpelSupport(runtimeHints);
|
||||
this.controllers.forEach(controller -> {
|
||||
this.controllers.forEach((controller) -> {
|
||||
runtimeHints.reflection().registerType(controller, MemberCategory.INTROSPECT_DECLARED_METHODS);
|
||||
ReflectionUtils.doWithMethods(controller,
|
||||
method -> processSchemaMappingMethod(runtimeHints, method),
|
||||
(method) -> processSchemaMappingMethod(runtimeHints, method),
|
||||
this::isGraphQlHandlerMethod);
|
||||
ReflectionUtils.doWithMethods(controller,
|
||||
method -> processExceptionHandlerMethod(runtimeHints, method),
|
||||
(method) -> processExceptionHandlerMethod(runtimeHints, method),
|
||||
this::isExceptionHandlerMethod);
|
||||
});
|
||||
this.controllerAdvices.forEach(controllerAdvice -> {
|
||||
this.controllerAdvices.forEach((controllerAdvice) -> {
|
||||
runtimeHints.reflection().registerType(controllerAdvice, MemberCategory.INTROSPECT_DECLARED_METHODS);
|
||||
ReflectionUtils.doWithMethods(controllerAdvice,
|
||||
method -> processExceptionHandlerMethod(runtimeHints, method),
|
||||
(method) -> processExceptionHandlerMethod(runtimeHints, method),
|
||||
this::isExceptionHandlerMethod);
|
||||
});
|
||||
}
|
||||
@@ -163,18 +160,18 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
runtimeHints.reflection()
|
||||
.registerType(SpelAwareProxyProjectionFactory.class)
|
||||
.registerType(TypeReference.of("org.springframework.data.projection.SpelEvaluatingMethodInterceptor$TargetWrapper"),
|
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGraphQlHandlerMethod(AnnotatedElement element) {
|
||||
MergedAnnotations annotations = MergedAnnotations.from(element, TYPE_HIERARCHY);
|
||||
MergedAnnotations annotations = MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
|
||||
return annotations.isPresent(SchemaMapping.class) || annotations.isPresent(BatchMapping.class);
|
||||
}
|
||||
|
||||
private boolean isExceptionHandlerMethod(AnnotatedElement element) {
|
||||
return MergedAnnotations.from(element, TYPE_HIERARCHY).isPresent(GraphQlExceptionHandler.class);
|
||||
return MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).isPresent(GraphQlExceptionHandler.class);
|
||||
}
|
||||
|
||||
private void processSchemaMappingMethod(RuntimeHints runtimeHints, Method method) {
|
||||
@@ -229,7 +226,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
}
|
||||
|
||||
|
||||
private static class NoHintsRequired implements MethodParameterRuntimeHintsRegistrar {
|
||||
private static final class NoHintsRequired implements MethodParameterRuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void apply(RuntimeHints runtimeHints) {
|
||||
@@ -242,14 +239,14 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
|
||||
private final MethodParameter methodParameter;
|
||||
|
||||
public ArgumentBindingHints(MethodParameter methodParameter) {
|
||||
ArgumentBindingHints(MethodParameter methodParameter) {
|
||||
this.methodParameter = methodParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(RuntimeHints runtimeHints) {
|
||||
Type parameterType = this.methodParameter.getGenericParameterType();
|
||||
if (ArgumentValue.class.isAssignableFrom(methodParameter.getParameterType())) {
|
||||
if (ArgumentValue.class.isAssignableFrom(this.methodParameter.getParameterType())) {
|
||||
parameterType = this.methodParameter.nested().getNestedGenericParameterType();
|
||||
}
|
||||
bindingRegistrar.registerReflectionHints(runtimeHints.reflection(), parameterType);
|
||||
@@ -261,7 +258,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
|
||||
private final MethodParameter methodParameter;
|
||||
|
||||
public DataLoaderHints(MethodParameter methodParameter) {
|
||||
DataLoaderHints(MethodParameter methodParameter) {
|
||||
this.methodParameter = methodParameter;
|
||||
}
|
||||
|
||||
@@ -277,7 +274,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
|
||||
|
||||
private final MethodParameter methodParameter;
|
||||
|
||||
public ProjectedPayloadHints(MethodParameter methodParameter) {
|
||||
ProjectedPayloadHints(MethodParameter methodParameter) {
|
||||
this.methodParameter = methodParameter;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,11 +57,11 @@ public class SortMethodArgumentResolver implements HandlerMethodArgumentResolver
|
||||
Sort sort = this.sortStrategy.extract(environment);
|
||||
|
||||
if (parameter.isOptional()) {
|
||||
sort = (sort == Sort.unsorted() ? null : sort);
|
||||
sort = (sort == Sort.unsorted()) ? null : sort;
|
||||
return Optional.ofNullable(sort);
|
||||
}
|
||||
|
||||
return (sort != null ? sort : Sort.unsorted());
|
||||
return (sort != null) ? sort : Sort.unsorted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.util.Assert;
|
||||
* Resolver for a method argument of type {@link Subrange} initialized
|
||||
* from "first", "last", "before", and "after" GraphQL arguments.
|
||||
*
|
||||
* @param <P> the type of position in the subrange
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@@ -62,12 +63,15 @@ public class SubrangeMethodArgumentResolver<P> implements HandlerMethodArgumentR
|
||||
forward = false;
|
||||
}
|
||||
}
|
||||
P pos = (cursor != null ? this.cursorStrategy.fromCursor(cursor) : null);
|
||||
P pos = (cursor != null) ? this.cursorStrategy.fromCursor(cursor) : null;
|
||||
return createSubrange(pos, count, forward);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to create an extension of {@link Subrange}.
|
||||
* @param pos the position in the subrange
|
||||
* @param count the number of elements in the subrange
|
||||
* @param forward whether the scroll direction is forward or backward from this position
|
||||
*/
|
||||
protected Subrange<P> createSubrange(@Nullable P pos, @Nullable Integer count, boolean forward) {
|
||||
return new Subrange<>(pos, count, forward);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -43,9 +44,8 @@ import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
* requires bean validation.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class ValidationHelper {
|
||||
final class ValidationHelper {
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
@@ -62,7 +62,7 @@ class ValidationHelper {
|
||||
* {@link Validated}, {@link Valid}, or {@link Constraint} annotations.
|
||||
*/
|
||||
@Nullable
|
||||
public BiConsumer<Object, Object[]> getValidationHelperFor(HandlerMethod handlerMethod) {
|
||||
BiConsumer<Object, Object[]> getValidationHelperFor(HandlerMethod handlerMethod) {
|
||||
|
||||
boolean requiresMethodValidation = false;
|
||||
Class<?>[] methodValidationGroups = null;
|
||||
@@ -88,18 +88,18 @@ class ValidationHelper {
|
||||
}
|
||||
else if (annot.annotationType().equals(Validated.class)) {
|
||||
Class<?>[] groups = ((Validated) annot).value();
|
||||
parameterValidator = (parameterValidator != null ?
|
||||
parameterValidator = (parameterValidator != null) ?
|
||||
parameterValidator.andThen(new MethodParameterValidator(i, groups)) :
|
||||
new MethodParameterValidator(i, groups));
|
||||
new MethodParameterValidator(i, groups);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BiConsumer<Object, Object[]> result = (requiresMethodValidation ?
|
||||
new HandlerMethodValidator(handlerMethod, methodValidationGroups) : null);
|
||||
BiConsumer<Object, Object[]> result = (requiresMethodValidation) ?
|
||||
new HandlerMethodValidator(handlerMethod, methodValidationGroups) : null;
|
||||
|
||||
if (parameterValidator != null) {
|
||||
return (result != null ? result.andThen(parameterValidator) : parameterValidator);
|
||||
return (result != null) ? result.andThen(parameterValidator) : parameterValidator;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -120,7 +120,7 @@ class ValidationHelper {
|
||||
* {@link Validator} bean declared, or {@code null} otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public static ValidationHelper createIfValidatorPresent(ApplicationContext context) {
|
||||
static ValidationHelper createIfValidatorPresent(ApplicationContext context) {
|
||||
Validator validator = context.getBeanProvider(Validator.class).getIfAvailable();
|
||||
if (validator instanceof LocalValidatorFactoryBean) {
|
||||
validator = ((LocalValidatorFactoryBean) validator).getValidator();
|
||||
@@ -128,13 +128,13 @@ class ValidationHelper {
|
||||
else if (validator instanceof SpringValidatorAdapter) {
|
||||
validator = validator.unwrap(Validator.class);
|
||||
}
|
||||
return (validator != null ? create(validator) : null);
|
||||
return (validator != null) ? create(validator) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method with a given {@link Validator} instance.
|
||||
*/
|
||||
public static ValidationHelper create(Validator validator) {
|
||||
static ValidationHelper create(Validator validator) {
|
||||
return new ValidationHelper(validator);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ class ValidationHelper {
|
||||
HandlerMethodValidator(HandlerMethod handlerMethod, @Nullable Class<?>[] validationGroups) {
|
||||
Assert.notNull(handlerMethod, "HandlerMethod is required");
|
||||
this.method = handlerMethod.getMethod();
|
||||
this.validationGroups = (validationGroups != null ? validationGroups : new Class<?>[] {});
|
||||
this.validationGroups = (validationGroups != null) ? validationGroups : new Class<?>[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,7 +181,7 @@ class ValidationHelper {
|
||||
|
||||
MethodParameterValidator(int index, @Nullable Class<?>[] validationGroups) {
|
||||
this.index = index;
|
||||
this.validationGroups = (validationGroups != null ? validationGroups : new Class<?>[] {});
|
||||
this.validationGroups = (validationGroups != null) ? validationGroups : new Class<?>[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolvers for method parameters of annotated handler methods.
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,6 @@ import java.util.Base64;
|
||||
* <p>To create an instance, use {@link CursorEncoder#base64()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
final class Base64CursorEncoder implements CursorEncoder {
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.util.Assert;
|
||||
* the first one that supports a given Object container type, and delegates to it.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
final class CompositeConnectionAdapter implements ConnectionAdapter {
|
||||
|
||||
@@ -45,18 +44,22 @@ final class CompositeConnectionAdapter implements ConnectionAdapter {
|
||||
return (getAdapter(containerType) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Collection<T> getContent(Object container) {
|
||||
return getRequiredAdapter(container).getContent(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious(Object container) {
|
||||
return getRequiredAdapter(container).hasPrevious(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext(Object container) {
|
||||
return getRequiredAdapter(container).hasNext(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cursorAt(Object container, int index) {
|
||||
return getRequiredAdapter(container).cursorAt(container, index);
|
||||
}
|
||||
|
||||
@@ -30,26 +30,33 @@ public interface ConnectionAdapter {
|
||||
|
||||
/**
|
||||
* Whether the adapter supports the given Object container type.
|
||||
* @param containerType the container type to check for support
|
||||
*/
|
||||
boolean supports(Class<?> containerType);
|
||||
|
||||
/**
|
||||
* Return the contained items as a List.
|
||||
* @param <T> the type of objects in the collection
|
||||
* @param container the container of elements
|
||||
*/
|
||||
<T> Collection<T> getContent(Object container);
|
||||
|
||||
/**
|
||||
* Whether there are more pages before this one.
|
||||
* @param container the container of elements
|
||||
*/
|
||||
boolean hasPrevious(Object container);
|
||||
|
||||
/**
|
||||
* Whether there are more pages after this one.
|
||||
* @param container the container of elements
|
||||
*/
|
||||
boolean hasNext(Object container);
|
||||
|
||||
/**
|
||||
* Return a cursor for the item at the given index.
|
||||
* @param container the container of elements
|
||||
* @param index the index of an element in the container
|
||||
*/
|
||||
String cursorAt(Object container, int index);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.util.Assert;
|
||||
* Convenient base class for implementations of
|
||||
* {@link org.springframework.graphql.data.pagination.ConnectionAdapter}.
|
||||
*
|
||||
* @param <P> the position type
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@@ -32,6 +33,7 @@ public class ConnectionAdapterSupport<P> {
|
||||
|
||||
/**
|
||||
* Constructor with a {@link CursorStrategy} to use.
|
||||
* @param cursorStrategy the cursor strategy to use
|
||||
*/
|
||||
protected ConnectionAdapterSupport(CursorStrategy<P> cursorStrategy) {
|
||||
Assert.notNull(cursorStrategy, "CursorStrategy is required");
|
||||
|
||||
@@ -143,7 +143,7 @@ public final class ConnectionFieldTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
|
||||
@Nullable
|
||||
private static GraphQLObjectType getAsObjectType(@Nullable GraphQLFieldDefinition field) {
|
||||
return (getType(field) instanceof GraphQLObjectType type ? type : null);
|
||||
return (getType(field) instanceof GraphQLObjectType type) ? type : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -162,7 +162,7 @@ public final class ConnectionFieldTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
return null;
|
||||
}
|
||||
GraphQLOutputType type = field.getType();
|
||||
return (type instanceof GraphQLNonNull nonNullType ? nonNullType.getWrappedType() : type);
|
||||
return (type instanceof GraphQLNonNull nonNullType) ? nonNullType.getWrappedType() : type;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ public final class ConnectionFieldTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
*/
|
||||
private record ConnectionDataFetcher(DataFetcher<?> delegate, ConnectionAdapter adapter) implements DataFetcher<Object> {
|
||||
|
||||
private final static Connection<?> EMPTY_CONNECTION =
|
||||
private static final Connection<?> EMPTY_CONNECTION =
|
||||
new DefaultConnection<>(Collections.emptyList(), new DefaultPageInfo(null, null, false, false));
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ package org.springframework.graphql.data.pagination;
|
||||
* {@link #withEncoder(CursorStrategy, CursorEncoder)} to further encode and
|
||||
* decode cursor Strings to make them opaque for clients.
|
||||
*
|
||||
* @param <P> the type of position
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@@ -31,6 +32,7 @@ public interface CursorStrategy<P> {
|
||||
|
||||
/**
|
||||
* Whether the strategy supports the given type of position Object.
|
||||
* @param targetType the type of position to be checked
|
||||
*/
|
||||
boolean supports(Class<?> targetType);
|
||||
|
||||
@@ -52,6 +54,9 @@ public interface CursorStrategy<P> {
|
||||
/**
|
||||
* Decorate the given {@code CursorStrategy} with encoding and decoding
|
||||
* that makes the String cursor opaque to clients.
|
||||
* @param <T> the type of position for the given strategy
|
||||
* @param strategy the cursor strategy to decorate
|
||||
* @param encoder strategy for encoding the cursor
|
||||
*/
|
||||
static <T> EncodingCursorStrategy<T> withEncoder(CursorStrategy<T> strategy, CursorEncoder encoder) {
|
||||
return new EncodingCursorStrategy<>(strategy, encoder);
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.util.Assert;
|
||||
* <p>To create an instance, use
|
||||
* {@link CursorStrategy#withEncoder(CursorStrategy, CursorEncoder)}.
|
||||
*
|
||||
* @param <T> the type of position
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,6 @@ package org.springframework.graphql.data.pagination;
|
||||
* <p>To create an instance, use {@link CursorEncoder#noOpEncoder()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
final class NoOpCursorEncoder implements CursorEncoder {
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.lang.Nullable;
|
||||
* Container for parameters that limit result elements to a subrange including a
|
||||
* relative position, number of elements, and direction.
|
||||
*
|
||||
* @param <P> the type of position in the entire collection
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@@ -41,10 +42,13 @@ public class Subrange<P> {
|
||||
|
||||
/**
|
||||
* Constructor with the relative position, count, and direction.
|
||||
* @param position the position in the entire collection
|
||||
* @param count the number of elements in the subrange
|
||||
* @param forward whether the subrange is forward or backward from ths position
|
||||
*/
|
||||
public Subrange(@Nullable P position, @Nullable Integer count, boolean forward) {
|
||||
this.position = Optional.ofNullable(position);
|
||||
this.count = (count != null ? OptionalInt.of(count) : OptionalInt.empty());
|
||||
this.count = (count != null) ? OptionalInt.of(count) : OptionalInt.empty();
|
||||
this.forward = forward;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class AbstractSortStrategy implements SortStrategy {
|
||||
List<String> properties = getProperties(environment);
|
||||
if (!ObjectUtils.isEmpty(properties)) {
|
||||
Sort.Direction direction = getDirection(environment);
|
||||
direction = (direction != null ? direction : Sort.DEFAULT_DIRECTION);
|
||||
direction = (direction != null) ? direction : Sort.DEFAULT_DIRECTION;
|
||||
List<Sort.Order> sortOrders = new ArrayList<>(properties.size());
|
||||
for (String property : properties) {
|
||||
sortOrders.add(new Sort.Order(direction, property));
|
||||
@@ -53,11 +53,13 @@ public abstract class AbstractSortStrategy implements SortStrategy {
|
||||
|
||||
/**
|
||||
* Return the sort properties to use, or an empty list if there are none.
|
||||
* @param environment the data fetching environment for this operation
|
||||
*/
|
||||
protected abstract List<String> getProperties(DataFetchingEnvironment environment);
|
||||
|
||||
/**
|
||||
* Return the sort direction to use, or {@code null}.
|
||||
* @param environment the data fetching environment for this operation
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Sort.Direction getDirection(DataFetchingEnvironment environment);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.query;
|
||||
|
||||
import java.util.List;
|
||||
@@ -42,11 +43,10 @@ import org.springframework.util.Assert;
|
||||
* already have registrations.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer {
|
||||
|
||||
private final static Log logger = LogFactory.getLog(AutoRegistrationRuntimeWiringConfigurer.class);
|
||||
private static final Log logger = LogFactory.getLog(AutoRegistrationRuntimeWiringConfigurer.class);
|
||||
|
||||
|
||||
private final Map<String, DataFetcherFactory> dataFetcherFactories;
|
||||
@@ -107,7 +107,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer
|
||||
|
||||
@Override
|
||||
public boolean providesDataFetcher(FieldWiringEnvironment environment) {
|
||||
if (dataFetcherFactories.isEmpty()) {
|
||||
if (AutoRegistrationRuntimeWiringConfigurer.this.dataFetcherFactories.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer
|
||||
String outputTypeName = getOutputTypeName(environment);
|
||||
|
||||
boolean result = (outputTypeName != null &&
|
||||
dataFetcherFactories.containsKey(outputTypeName) &&
|
||||
AutoRegistrationRuntimeWiringConfigurer.this.dataFetcherFactories.containsKey(outputTypeName) &&
|
||||
!hasDataFetcherFor(environment.getFieldDefinition()));
|
||||
|
||||
if (!result) {
|
||||
@@ -150,7 +150,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer
|
||||
}
|
||||
|
||||
private GraphQLType removeNonNullWrapper(GraphQLType outputType) {
|
||||
return (outputType instanceof GraphQLNonNull wrapper ? wrapper.getWrappedType() : outputType);
|
||||
return (outputType instanceof GraphQLNonNull wrapper) ? wrapper.getWrappedType() : outputType;
|
||||
}
|
||||
|
||||
private boolean isConnectionType(GraphQLType type) {
|
||||
@@ -162,7 +162,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer
|
||||
private boolean hasDataFetcherFor(FieldDefinition fieldDefinition) {
|
||||
if (this.existingQueryDataFetcherPredicate == null) {
|
||||
Map<String, ?> map = this.builder.build().getDataFetcherForType("Query");
|
||||
this.existingQueryDataFetcherPredicate = fieldName -> map.get(fieldName) != null;
|
||||
this.existingQueryDataFetcherPredicate = (fieldName) -> map.get(fieldName) != null;
|
||||
}
|
||||
return this.existingQueryDataFetcherPredicate.test(fieldDefinition.getName());
|
||||
}
|
||||
@@ -171,7 +171,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer
|
||||
if (logger.isTraceEnabled()) {
|
||||
String query = environment.getFieldDefinition().getName();
|
||||
logger.trace((match ? "Matched" : "Skipped") +
|
||||
" output typeName " + (typeName != null ? "'" + typeName + "'" : "null") +
|
||||
" output typeName " + ((typeName != null) ? "'" + typeName + "'" : "null") +
|
||||
" for query '" + query + "'");
|
||||
}
|
||||
}
|
||||
@@ -182,12 +182,12 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer
|
||||
String outputTypeName = getOutputTypeName(environment);
|
||||
logTraceMessage(environment, outputTypeName, true);
|
||||
|
||||
DataFetcherFactory factory = dataFetcherFactories.get(outputTypeName);
|
||||
DataFetcherFactory factory = AutoRegistrationRuntimeWiringConfigurer.this.dataFetcherFactories.get(outputTypeName);
|
||||
Assert.notNull(factory, "Expected DataFetcher factory for typeName '" + outputTypeName + "'");
|
||||
|
||||
GraphQLType type = removeNonNullWrapper(environment.getFieldType());
|
||||
return (isConnectionType(type) ? factory.scrollable() :
|
||||
(type instanceof GraphQLList ? factory.many() : factory.single()));
|
||||
(type instanceof GraphQLList) ? factory.many() : factory.single());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.query;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -39,9 +40,9 @@ import org.springframework.lang.Nullable;
|
||||
* already have registrations.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @deprecated in favor of {@link AutoRegistrationRuntimeWiringConfigurer}
|
||||
* @deprecated since 1.0.0 in favor of {@link AutoRegistrationRuntimeWiringConfigurer}
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since = "1.0.0", forRemoval = true)
|
||||
class AutoRegistrationTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
|
||||
private final Map<String, Function<Boolean, DataFetcher<?>>> dataFetcherFactories;
|
||||
@@ -52,7 +53,7 @@ class AutoRegistrationTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
* @param dataFetcherFactories map with GraphQL type names as keys and
|
||||
* functions as values to create a DataFetcher for single or many values
|
||||
*/
|
||||
public AutoRegistrationTypeVisitor(Map<String, Function<Boolean, DataFetcher<?>>> dataFetcherFactories) {
|
||||
AutoRegistrationTypeVisitor(Map<String, Function<Boolean, DataFetcher<?>>> dataFetcherFactories) {
|
||||
this.dataFetcherFactories = dataFetcherFactories;
|
||||
}
|
||||
|
||||
@@ -71,9 +72,9 @@ class AutoRegistrationTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
return TraversalControl.ABORT;
|
||||
}
|
||||
|
||||
DataFetcher<?> dataFetcher = (fieldType instanceof GraphQLList ?
|
||||
DataFetcher<?> dataFetcher = (fieldType instanceof GraphQLList) ?
|
||||
getDataFetcher(((GraphQLList) fieldType).getWrappedType(), false) :
|
||||
getDataFetcher(fieldType, true));
|
||||
getDataFetcher(fieldType, true);
|
||||
|
||||
if (dataFetcher != null) {
|
||||
GraphQLCodeRegistry.Builder registry = context.getVarFromParents(GraphQLCodeRegistry.Builder.class);
|
||||
|
||||
@@ -86,6 +86,7 @@ public final class JsonKeysetCursorStrategy implements CursorStrategy<Map<String
|
||||
/**
|
||||
* Constructor with a {@link CodecConfigurer} in which to find the JSON
|
||||
* encoder and decoder to use.
|
||||
* @param codecConfigurer the codec configurer to be checked for JSON codec
|
||||
*/
|
||||
public JsonKeysetCursorStrategy(CodecConfigurer codecConfigurer) {
|
||||
Assert.notNull(codecConfigurer, "CodecConfigurer is required");
|
||||
@@ -128,7 +129,7 @@ public final class JsonKeysetCursorStrategy implements CursorStrategy<Map<String
|
||||
public Map<String, Object> fromCursor(String cursor) {
|
||||
DataBuffer buffer = this.bufferFactory.wrap(cursor.getBytes(StandardCharsets.UTF_8));
|
||||
Map<String, Object> map = ((Decoder<Map<String, Object>>) this.decoder).decode(buffer, MAP_TYPE, null, null);
|
||||
return (map != null ? map : Collections.emptyMap());
|
||||
return (map != null) ? map : Collections.emptyMap();
|
||||
}
|
||||
|
||||
|
||||
@@ -136,9 +137,9 @@ public final class JsonKeysetCursorStrategy implements CursorStrategy<Map<String
|
||||
* Customizes the {@link ObjectMapper} to use default typing that supports
|
||||
* {@link Date}, {@link Calendar}, and classes in {@code java.time}.
|
||||
*/
|
||||
private static class JacksonObjectMapperCustomizer {
|
||||
private static final class JacksonObjectMapperCustomizer {
|
||||
|
||||
public static void customize(CodecConfigurer configurer) {
|
||||
static void customize(CodecConfigurer configurer) {
|
||||
|
||||
PolymorphicTypeValidator validator = BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfBaseType(Map.class)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2020-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -41,9 +41,8 @@ import org.springframework.util.CollectionUtils;
|
||||
* is considered to be a composite property without further inspection.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class PropertySelection {
|
||||
final class PropertySelection {
|
||||
|
||||
private final List<PropertyPath> propertyPaths;
|
||||
|
||||
@@ -54,9 +53,9 @@ class PropertySelection {
|
||||
|
||||
|
||||
/**
|
||||
* @return the property paths as list.
|
||||
* Return the property paths as list.
|
||||
*/
|
||||
public List<String> toList() {
|
||||
List<String> toList() {
|
||||
return this.propertyPaths.stream().map(PropertyPath::toDotPath).toList();
|
||||
}
|
||||
|
||||
@@ -64,14 +63,13 @@ class PropertySelection {
|
||||
/**
|
||||
* Create a property selection for the given {@link TypeInformation type} and
|
||||
* {@link DataFetchingFieldSelectionSet}.
|
||||
*
|
||||
* @param typeInfo the type to inspect
|
||||
* @param selectionSet the field selection to apply
|
||||
* @return a property selection holding all selectable property paths.
|
||||
*/
|
||||
public static PropertySelection create(TypeInformation<?> typeInfo, DataFetchingFieldSelectionSet selectionSet) {
|
||||
static PropertySelection create(TypeInformation<?> typeInfo, DataFetchingFieldSelectionSet selectionSet) {
|
||||
FieldSelection selection = new DataFetchingFieldSelection(selectionSet);
|
||||
List<PropertyPath> paths = getPropertyPaths(typeInfo, selection, path -> PropertyPath.from(path, typeInfo));
|
||||
List<PropertyPath> paths = getPropertyPaths(typeInfo, selection, (path) -> PropertyPath.from(path, typeInfo));
|
||||
return new PropertySelection(paths);
|
||||
}
|
||||
|
||||
@@ -111,8 +109,8 @@ class PropertySelection {
|
||||
|
||||
private static boolean isConnectionEdges(SelectedField selectedField) {
|
||||
return selectedField.getName().equals("edges") &&
|
||||
selectedField.getParentField().getType() instanceof GraphQLNamedOutputType namedType &&
|
||||
namedType.getName().endsWith("Connection");
|
||||
selectedField.getParentField().getType() instanceof GraphQLNamedOutputType namedType &&
|
||||
namedType.getName().endsWith("Connection");
|
||||
}
|
||||
|
||||
private static boolean isConnectionEdgeNode(SelectedField selectedField) {
|
||||
@@ -141,13 +139,12 @@ class PropertySelection {
|
||||
interface FieldSelection extends Iterable<SelectedField> {
|
||||
|
||||
/**
|
||||
* @return {@code true} if the field selection is empty
|
||||
* Return {@code true} if the field selection is empty.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Obtain the field selection (nested fields) for a given {@code field}.
|
||||
*
|
||||
* @param field the field for which nested fields should be obtained
|
||||
* @return the field selection. Can be empty.
|
||||
*/
|
||||
@@ -174,7 +171,7 @@ class PropertySelection {
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return selectedFields.isEmpty();
|
||||
return this.selectedFields.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -183,14 +180,14 @@ class PropertySelection {
|
||||
|
||||
for (SelectedField selectedField : this.allFields) {
|
||||
if (field.equals(selectedField.getParentField())) {
|
||||
selectedFields = (selectedFields != null ? selectedFields : new ArrayList<>());
|
||||
selectedFields = (selectedFields != null) ? selectedFields : new ArrayList<>();
|
||||
selectedFields.add(selectedField);
|
||||
}
|
||||
}
|
||||
|
||||
return (selectedFields != null ?
|
||||
return (selectedFields != null) ?
|
||||
new DataFetchingFieldSelection(selectedFields, this.allFields) :
|
||||
EmptyFieldSelection.INSTANCE);
|
||||
EmptyFieldSelection.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,7 +101,7 @@ import org.springframework.validation.BindException;
|
||||
*/
|
||||
public abstract class QueryByExampleDataFetcher<T> {
|
||||
|
||||
private final static Log logger = LogFactory.getLog(QueryByExampleDataFetcher.class);
|
||||
private static final Log logger = LogFactory.getLog(QueryByExampleDataFetcher.class);
|
||||
|
||||
|
||||
private final TypeInformation<T> domainType;
|
||||
@@ -147,7 +147,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
List<GraphQLArgument> definedArguments = environment.getFieldDefinition().getArguments();
|
||||
if (definedArguments.size() == 1) {
|
||||
String name = definedArguments.get(0).getName();
|
||||
if (arguments.get(name) instanceof Map<?,?>) {
|
||||
if (arguments.get(name) instanceof Map<?, ?>) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* without a {@code CursorStrategy} and default {@link ScrollSubrange}.
|
||||
* For default values, see the respective methods on {@link Builder} and
|
||||
* {@link ReactiveBuilder}.
|
||||
* @param executors repositories to consider for registration
|
||||
* @param reactiveExecutors reactive repositories to consider for registration
|
||||
*/
|
||||
public static RuntimeWiringConfigurer autoRegistrationConfigurer(
|
||||
List<QueryByExampleExecutor<?>> executors,
|
||||
@@ -215,10 +217,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* {@link graphql.schema.idl.WiringFactory} to find queries with a return
|
||||
* type whose name matches to the domain type name of the given repositories
|
||||
* and registers {@link DataFetcher}s for them.
|
||||
*
|
||||
* <p><strong>Note:</strong> This applies only to top-level queries and
|
||||
* repositories annotated with {@link GraphQlRepository @GraphQlRepository}.
|
||||
*
|
||||
* @param executors repositories to consider for registration
|
||||
* @param reactiveExecutors reactive repositories to consider for registration
|
||||
* @param cursorStrategy for decoding cursors in pagination requests;
|
||||
@@ -301,13 +301,13 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* registers {@link DataFetcher}s for those queries.
|
||||
* <p><strong>Note:</strong> currently, this method will match only to
|
||||
* queries under the top-level "Query" type in the GraphQL schema.
|
||||
*
|
||||
* @param executors repositories to consider for registration
|
||||
* @param reactiveExecutors reactive repositories to consider for registration
|
||||
* @return the created visitor
|
||||
* @deprecated in favor of {@link #autoRegistrationConfigurer(List, List)}
|
||||
* @deprecated since 1.0.0, in favor of {@link #autoRegistrationConfigurer(List, List)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since = "1.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
public static GraphQLTypeVisitor autoRegistrationTypeVisitor(
|
||||
List<QueryByExampleExecutor<?>> executors,
|
||||
List<ReactiveQueryByExampleExecutor<?>> reactiveExecutors) {
|
||||
@@ -318,7 +318,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
String typeName = RepositoryUtils.getGraphQlTypeName(executor);
|
||||
if (typeName != null) {
|
||||
Builder<?, ?> builder = customize(executor, builder(executor));
|
||||
factories.put(typeName, single -> single ? builder.single() : builder.many());
|
||||
factories.put(typeName, (single) -> single ? builder.single() : builder.many());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
String typeName = RepositoryUtils.getGraphQlTypeName(executor);
|
||||
if (typeName != null) {
|
||||
ReactiveBuilder<?, ?> builder = customize(executor, builder(executor));
|
||||
factories.put(typeName, single -> single ? builder.single() : builder.many());
|
||||
factories.put(typeName, (single) -> single ? builder.single() : builder.many());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static Builder customize(QueryByExampleExecutor<?> executor, Builder builder) {
|
||||
if(executor instanceof QueryByExampleBuilderCustomizer<?> customizer){
|
||||
if (executor instanceof QueryByExampleBuilderCustomizer<?> customizer) {
|
||||
return customizer.customize(builder);
|
||||
}
|
||||
return builder;
|
||||
@@ -344,7 +344,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static ReactiveBuilder customize(ReactiveQueryByExampleExecutor<?> executor, ReactiveBuilder builder) {
|
||||
if(executor instanceof ReactiveQueryByExampleBuilderCustomizer<?> customizer){
|
||||
if (executor instanceof ReactiveQueryByExampleBuilderCustomizer<?> customizer) {
|
||||
return customizer.customize(builder);
|
||||
}
|
||||
return builder;
|
||||
@@ -402,6 +402,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* into the target {@code projectionType}. Projection types can be
|
||||
* either interfaces with property getters to expose or regular classes
|
||||
* outside the entity type hierarchy for DTO projections.
|
||||
* @param <P> the result type
|
||||
* @param projectionType projection type
|
||||
* @return a new {@link Builder} instance with all previously
|
||||
* configured options and {@code projectionType} applied
|
||||
@@ -435,6 +436,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @param defaultCount the default scroll count to use
|
||||
* @param defaultPosition a function that returns the forward/backward scroll position
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public Builder<T, R> defaultScrollSubrange(
|
||||
@@ -449,6 +452,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* not specify a cursor and/or a count of items.
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a
|
||||
* count of 20.
|
||||
* @param defaultSubrange the default scroll subrange to use, can be {@code null}
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
* options and {@code Sort} applied
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
@@ -458,8 +462,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new Builder<>(this.executor, this.domainType,
|
||||
this.resultType, this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
(defaultSubrange != null) ? defaultSubrange.count().getAsInt() : null,
|
||||
(defaultSubrange != null) ? (forward) -> defaultSubrange.position().get() : null,
|
||||
this.sort);
|
||||
}
|
||||
|
||||
@@ -497,9 +501,9 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
public DataFetcher<Iterable<R>> scrollable() {
|
||||
return new ScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
(this.cursorStrategy != null) ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy(),
|
||||
(this.defaultScrollCount != null) ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount(),
|
||||
(this.defaultScrollPosition != null) ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition(),
|
||||
this.sort);
|
||||
}
|
||||
|
||||
@@ -511,8 +515,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* <p>This is supported by {@link #autoRegistrationConfigurer(List, List)
|
||||
* Auto-registration}, which detects if a repository implements this
|
||||
* interface and applies it accordingly.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> domain type
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public interface QueryByExampleBuilderCustomizer<T> {
|
||||
@@ -578,13 +581,14 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* into the target {@code projectionType}. Projection types can be
|
||||
* either interfaces with property getters to expose or regular classes
|
||||
* outside the entity type hierarchy for DTO projections.
|
||||
* @param <P> the result type
|
||||
* @param projectionType projection type
|
||||
* @return a new {@link ReactiveBuilder} instance with all previously
|
||||
* configured options and {@code projectionType} applied
|
||||
*/
|
||||
public <P> ReactiveBuilder<T, P> projectAs(Class<P> projectionType) {
|
||||
Assert.notNull(projectionType, "Projection type must not be null");
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
projectionType, this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, this.sort);
|
||||
}
|
||||
|
||||
@@ -611,6 +615,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @param defaultCount the default scroll count to use
|
||||
* @param defaultPosition a function that returns the forward/backward scroll position
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(
|
||||
@@ -625,6 +631,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* not specify a cursor and/or a count of items.
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a
|
||||
* count of 20.
|
||||
* @param defaultSubrange the default scroll subrange to use, can be {@code null}
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
* options and {@code Sort} applied
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
@@ -634,8 +641,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
this.resultType, this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
(defaultSubrange != null) ? defaultSubrange.count().getAsInt() : null,
|
||||
(defaultSubrange != null) ? (forward) -> defaultSubrange.position().get() : null,
|
||||
this.sort);
|
||||
}
|
||||
|
||||
@@ -673,9 +680,9 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
public DataFetcher<Mono<Iterable<R>>> scrollable() {
|
||||
return new ReactiveScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
(this.cursorStrategy != null) ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy(),
|
||||
(this.defaultScrollCount != null) ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount(),
|
||||
(this.defaultScrollPosition != null) ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition(),
|
||||
this.sort);
|
||||
}
|
||||
|
||||
@@ -687,8 +694,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* <p>This is supported by {@link #autoRegistrationConfigurer(List, List)
|
||||
* Auto-registration}, which detects if a repository implements this
|
||||
* interface and applies it accordingly.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> the domain type
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public interface ReactiveQueryByExampleBuilderCustomizer<T> {
|
||||
@@ -728,7 +734,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings({"ConstantConditions", "unchecked"})
|
||||
public R get(DataFetchingEnvironment env) throws BindException {
|
||||
return this.executor.findBy(buildExample(env), query -> {
|
||||
return this.executor.findBy(buildExample(env), (query) -> {
|
||||
FluentQuery.FetchableFluentQuery<R> queryToUse = (FluentQuery.FetchableFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()) {
|
||||
@@ -777,7 +783,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterable<R> get(DataFetchingEnvironment env) throws BindException {
|
||||
return this.executor.findBy(buildExample(env), query -> {
|
||||
return this.executor.findBy(buildExample(env), (query) -> {
|
||||
FluentQuery.FetchableFluentQuery<R> queryToUse = (FluentQuery.FetchableFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()) {
|
||||
@@ -874,7 +880,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<R> get(DataFetchingEnvironment env) throws BindException {
|
||||
return this.executor.findBy(buildExample(env), query -> {
|
||||
return this.executor.findBy(buildExample(env), (query) -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()) {
|
||||
@@ -922,7 +928,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Flux<R> get(DataFetchingEnvironment env) throws BindException {
|
||||
return this.executor.findBy(buildExample(env), query -> {
|
||||
return this.executor.findBy(buildExample(env), (query) -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()) {
|
||||
@@ -989,7 +995,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Iterable<R>> get(DataFetchingEnvironment env) throws BindException {
|
||||
return this.executor.findBy(buildExample(env), query -> {
|
||||
return this.executor.findBy(buildExample(env), (query) -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()) {
|
||||
|
||||
@@ -107,7 +107,7 @@ import org.springframework.util.MultiValueMap;
|
||||
*/
|
||||
public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
private final static Log logger = LogFactory.getLog(QueryByExampleDataFetcher.class);
|
||||
private static final Log logger = LogFactory.getLog(QueryByExampleDataFetcher.class);
|
||||
|
||||
private static final QuerydslPredicateBuilder BUILDER = new QuerydslPredicateBuilder(
|
||||
DefaultConversionService.getSharedInstance(), SimpleEntityPathResolver.INSTANCE);
|
||||
@@ -153,7 +153,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
for (Map.Entry<String, Object> entry : getArgumentValues(environment).entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
List<Object> values = (value instanceof List ? (List<Object>) value : Collections.singletonList(value));
|
||||
List<Object> values = (value instanceof List) ? (List<Object>) value : Collections.singletonList(value);
|
||||
parameters.put(entry.getKey(), values);
|
||||
}
|
||||
|
||||
@@ -225,6 +225,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* without a {@code CursorStrategy} and default {@link ScrollSubrange}.
|
||||
* For default values, see the respective methods on {@link Builder} and
|
||||
* {@link ReactiveBuilder}.
|
||||
* @param executors repositories to consider for registration
|
||||
* @param reactiveExecutors reactive repositories to consider for registration
|
||||
*/
|
||||
public static RuntimeWiringConfigurer autoRegistrationConfigurer(
|
||||
List<QuerydslPredicateExecutor<?>> executors,
|
||||
@@ -244,7 +246,6 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* If a repository is also an instance of {@link QuerydslBinderCustomizer},
|
||||
* this is transparently detected and applied through the
|
||||
* {@code QuerydslDataFetcher} builder methods.
|
||||
*
|
||||
* @param executors repositories to consider for registration
|
||||
* @param reactiveExecutors reactive repositories to consider for registration
|
||||
* @param cursorStrategy for decoding cursors in pagination requests;
|
||||
@@ -337,14 +338,13 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* If a repository is also an instance of {@link QuerydslBinderCustomizer},
|
||||
* this is transparently detected and applied through the
|
||||
* {@code QuerydslDataFetcher} builder methods.
|
||||
*
|
||||
* @param executors repositories to consider for registration
|
||||
* @param reactiveExecutors reactive repositories to consider for registration
|
||||
* @return the created visitor
|
||||
* @deprecated in favor of {@link #autoRegistrationConfigurer(List, List)}
|
||||
* @deprecated since 1.0.0 in favor of {@link #autoRegistrationConfigurer(List, List)}
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Deprecated
|
||||
@SuppressWarnings({"unchecked", "rawtypes", "removal"})
|
||||
@Deprecated(since = "1.0.0", forRemoval = true)
|
||||
public static GraphQLTypeVisitor autoRegistrationTypeVisitor(
|
||||
List<QuerydslPredicateExecutor<?>> executors,
|
||||
List<ReactiveQuerydslPredicateExecutor<?>> reactiveExecutors) {
|
||||
@@ -355,7 +355,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
String typeName = RepositoryUtils.getGraphQlTypeName(executor);
|
||||
if (typeName != null) {
|
||||
Builder<?, ?> builder = customize(executor, QuerydslDataFetcher.builder(executor).customizer(customizer(executor)));
|
||||
factories.put(typeName, single -> single ? builder.single() : builder.many());
|
||||
factories.put(typeName, (single) -> single ? builder.single() : builder.many());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
String typeName = RepositoryUtils.getGraphQlTypeName(executor);
|
||||
if (typeName != null) {
|
||||
ReactiveBuilder builder = customize(executor, QuerydslDataFetcher.builder(executor).customizer(customizer(executor)));
|
||||
factories.put(typeName, single -> single ? builder.single() : builder.many());
|
||||
factories.put(typeName, (single) -> single ? builder.single() : builder.many());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static Builder customize(QuerydslPredicateExecutor<?> executor, Builder builder) {
|
||||
if(executor instanceof QuerydslBuilderCustomizer<?> customizer){
|
||||
if (executor instanceof QuerydslBuilderCustomizer<?> customizer) {
|
||||
return customizer.customize(builder);
|
||||
}
|
||||
return builder;
|
||||
@@ -380,7 +380,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static ReactiveBuilder customize(ReactiveQuerydslPredicateExecutor<?> executor, ReactiveBuilder builder) {
|
||||
if(executor instanceof ReactiveQuerydslBuilderCustomizer<?> customizer){
|
||||
if (executor instanceof ReactiveQuerydslBuilderCustomizer<?> customizer) {
|
||||
return customizer.customize(builder);
|
||||
}
|
||||
return builder;
|
||||
@@ -388,9 +388,9 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static QuerydslBinderCustomizer customizer(Object executor) {
|
||||
return (executor instanceof QuerydslBinderCustomizer<?> ?
|
||||
return (executor instanceof QuerydslBinderCustomizer<?>) ?
|
||||
(QuerydslBinderCustomizer<? extends EntityPath<?>>) executor :
|
||||
NO_OP_BINDER_CUSTOMIZER);
|
||||
NO_OP_BINDER_CUSTOMIZER;
|
||||
}
|
||||
|
||||
|
||||
@@ -448,6 +448,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* into the target {@code projectionType}. Projection types can be
|
||||
* either interfaces with property getters to expose or regular classes
|
||||
* outside the entity type hierarchy for DTO projections.
|
||||
* @param <P> the result type
|
||||
* @param projectionType projection type
|
||||
* @return a new {@link Builder} instance with all previously
|
||||
* configured options and {@code projectionType} applied
|
||||
@@ -483,6 +484,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @param defaultCount the default scroll count to use
|
||||
* @param defaultPosition the function that returns the forward/backward scroll position.
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public Builder<T, R> defaultScrollSubrange(
|
||||
@@ -496,6 +499,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* Configure a {@link ScrollSubrange} to use when a paginated request does
|
||||
* not specify a cursor and/or a count of items.
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a count of 20.
|
||||
* @param defaultSubrange the default scroll subrange to use, can be {@code null}
|
||||
* @return a new {@link Builder} instance
|
||||
* @since 1.2.0
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
@@ -504,8 +508,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@Deprecated(since = "1.2.5", forRemoval = true)
|
||||
public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType, this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
(defaultSubrange != null) ? defaultSubrange.count().getAsInt() : null,
|
||||
(defaultSubrange != null) ? (forward) -> defaultSubrange.position().get() : null,
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
@@ -519,7 +523,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
sort, customizer);
|
||||
sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,7 +533,6 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* itself, this is automatically detected and applied during
|
||||
* {@link #autoRegistrationConfigurer(List, List) auto-registration}.
|
||||
* For manual registration, you will need to use this method to apply it.
|
||||
*
|
||||
* @param customizer to customize the binding of the GraphQL request to
|
||||
* Querydsl Predicate
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
@@ -566,9 +569,9 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public DataFetcher<Iterable<R>> scrollable() {
|
||||
return new ScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
(this.cursorStrategy != null) ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy(),
|
||||
(this.defaultScrollCount != null) ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount(),
|
||||
(this.defaultScrollPosition != null) ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition(),
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
@@ -577,12 +580,11 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize QuerydslDataFetcher
|
||||
* {@link Builder} to change its configuration.
|
||||
* {@link Builder} to change its configuration.
|
||||
* <p>This is supported by {@link #autoRegistrationConfigurer(List, List)
|
||||
* Auto-registration}, which detects if a repository implements this
|
||||
* interface and applies it accordingly.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> the domain type
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public interface QuerydslBuilderCustomizer<T> {
|
||||
@@ -651,6 +653,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* into the target {@code projectionType}. Projection types can be
|
||||
* either interfaces with property getters to expose or regular classes
|
||||
* outside the entity type hierarchy for DTO projections.
|
||||
* @param <P> the project type
|
||||
* @param projectionType projection type
|
||||
* @return a new {@link Builder} instance with all previously
|
||||
* configured options and {@code projectionType} applied
|
||||
@@ -686,6 +689,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @param defaultCount the default scroll count to use
|
||||
* @param defaultPosition a function that returns the forward/backward scroll position
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(
|
||||
@@ -699,6 +704,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* Configure a {@link ScrollSubrange} to use when a paginated request does
|
||||
* not specify a cursor and/or a count of items.
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a count of 20.
|
||||
* @param defaultSubrange the default scroll subrange to use, can be {@code null}
|
||||
* @return a new {@link Builder} instance
|
||||
* @since 1.2.0
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
@@ -708,8 +714,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
(defaultSubrange != null) ? defaultSubrange.count().getAsInt() : null,
|
||||
(defaultSubrange != null) ? (forward) -> defaultSubrange.position().get() : null,
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
@@ -733,7 +739,6 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* itself, this is automatically detected and applied during
|
||||
* {@link #autoRegistrationConfigurer(List, List) auto-registration}.
|
||||
* For manual registration, you will need to use this method to apply it.
|
||||
*
|
||||
* @param customizer to customize the GraphQL query to Querydsl
|
||||
* Predicate binding with
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
@@ -770,9 +775,9 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public DataFetcher<Mono<Iterable<R>>> scrollable() {
|
||||
return new ReactiveScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
(this.cursorStrategy != null) ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy(),
|
||||
(this.defaultScrollCount != null) ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount(),
|
||||
(this.defaultScrollPosition != null) ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition(),
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
@@ -786,7 +791,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
* Auto-registration}, which detects if a repository implements this
|
||||
* interface and applies it accordingly.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> the domain type
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public interface ReactiveQuerydslBuilderCustomizer<T> {
|
||||
@@ -828,15 +833,15 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings({"ConstantConditions", "unchecked"})
|
||||
public R get(DataFetchingEnvironment env) {
|
||||
return this.executor.findBy(buildPredicate(env), query -> {
|
||||
return this.executor.findBy(buildPredicate(env), (query) -> {
|
||||
FetchableFluentQuery<R> queryToUse = (FetchableFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()){
|
||||
if (this.sort.isSorted()) {
|
||||
queryToUse = queryToUse.sortBy(this.sort);
|
||||
}
|
||||
|
||||
Class<R> resultType = this.resultType;
|
||||
if (requiresProjection(resultType)){
|
||||
if (requiresProjection(resultType)) {
|
||||
queryToUse = queryToUse.as(resultType);
|
||||
}
|
||||
else {
|
||||
@@ -878,14 +883,14 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterable<R> get(DataFetchingEnvironment env) {
|
||||
return this.executor.findBy(buildPredicate(env), query -> {
|
||||
return this.executor.findBy(buildPredicate(env), (query) -> {
|
||||
FetchableFluentQuery<R> queryToUse = (FetchableFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()){
|
||||
if (this.sort.isSorted()) {
|
||||
queryToUse = queryToUse.sortBy(this.sort);
|
||||
}
|
||||
|
||||
if (requiresProjection(this.resultType)){
|
||||
if (requiresProjection(this.resultType)) {
|
||||
queryToUse = queryToUse.as(this.resultType);
|
||||
}
|
||||
else {
|
||||
@@ -971,14 +976,14 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<R> get(DataFetchingEnvironment env) {
|
||||
return this.executor.findBy(buildPredicate(env), query -> {
|
||||
return this.executor.findBy(buildPredicate(env), (query) -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()){
|
||||
if (this.sort.isSorted()) {
|
||||
queryToUse = queryToUse.sortBy(this.sort);
|
||||
}
|
||||
|
||||
if (requiresProjection(this.resultType)){
|
||||
if (requiresProjection(this.resultType)) {
|
||||
queryToUse = queryToUse.as(this.resultType);
|
||||
}
|
||||
else {
|
||||
@@ -1021,14 +1026,14 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Flux<R> get(DataFetchingEnvironment env) {
|
||||
return this.executor.findBy(buildPredicate(env), query -> {
|
||||
return this.executor.findBy(buildPredicate(env), (query) -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()){
|
||||
if (this.sort.isSorted()) {
|
||||
queryToUse = queryToUse.sortBy(this.sort);
|
||||
}
|
||||
|
||||
if (requiresProjection(this.resultType)){
|
||||
if (requiresProjection(this.resultType)) {
|
||||
queryToUse = queryToUse.as(this.resultType);
|
||||
}
|
||||
else {
|
||||
@@ -1091,14 +1096,14 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Iterable<R>> get(DataFetchingEnvironment env) {
|
||||
return this.executor.findBy(buildPredicate(env), query -> {
|
||||
return this.executor.findBy(buildPredicate(env), (query) -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
|
||||
if (this.sort.isSorted()){
|
||||
if (this.sort.isSorted()) {
|
||||
queryToUse = queryToUse.sortBy(this.sort);
|
||||
}
|
||||
|
||||
if (requiresProjection(this.resultType)){
|
||||
if (requiresProjection(this.resultType)) {
|
||||
queryToUse = queryToUse.as(this.resultType);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.query;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
@@ -40,16 +41,19 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Oliver Drotbohm
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class RepositoryUtils {
|
||||
final class RepositoryUtils {
|
||||
|
||||
private RepositoryUtils() {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> getDomainType(Object executor) {
|
||||
static <T> Class<T> getDomainType(Object executor) {
|
||||
return (Class<T>) getRepositoryMetadata(executor).getDomainType();
|
||||
}
|
||||
|
||||
public static RepositoryMetadata getRepositoryMetadata(Object executor) {
|
||||
static RepositoryMetadata getRepositoryMetadata(Object executor) {
|
||||
Assert.isInstanceOf(Repository.class, executor);
|
||||
|
||||
Type[] genericInterfaces = executor.getClass().getGenericInterfaces();
|
||||
@@ -68,7 +72,7 @@ class RepositoryUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getGraphQlTypeName(Object repository) {
|
||||
static String getGraphQlTypeName(Object repository) {
|
||||
GraphQlRepository annotation =
|
||||
AnnotatedElementUtils.findMergedAnnotation(repository.getClass(), GraphQlRepository.class);
|
||||
|
||||
@@ -81,19 +85,19 @@ class RepositoryUtils {
|
||||
}
|
||||
|
||||
|
||||
public static CursorStrategy<ScrollPosition> defaultCursorStrategy() {
|
||||
static CursorStrategy<ScrollPosition> defaultCursorStrategy() {
|
||||
return CursorStrategy.withEncoder(new ScrollPositionCursorStrategy(), CursorEncoder.base64());
|
||||
}
|
||||
|
||||
public static int defaultScrollCount() {
|
||||
static int defaultScrollCount() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
public static Function<Boolean, ScrollPosition> defaultScrollPosition() {
|
||||
return forward -> ScrollPosition.offset();
|
||||
static Function<Boolean, ScrollPosition> defaultScrollPosition() {
|
||||
return (forward) -> ScrollPosition.offset();
|
||||
}
|
||||
|
||||
public static ScrollSubrange getScrollSubrange(
|
||||
static ScrollSubrange getScrollSubrange(
|
||||
DataFetchingEnvironment env, CursorStrategy<ScrollPosition> cursorStrategy) {
|
||||
|
||||
boolean forward = true;
|
||||
@@ -106,7 +110,7 @@ class RepositoryUtils {
|
||||
forward = false;
|
||||
}
|
||||
}
|
||||
ScrollPosition pos = (cursor != null ? cursorStrategy.fromCursor(cursor) : null);
|
||||
ScrollPosition pos = (cursor != null) ? cursorStrategy.fromCursor(cursor) : null;
|
||||
return ScrollSubrange.create(pos, count, forward);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ public final class ScrollPositionCursorStrategy implements CursorStrategy<Scroll
|
||||
/**
|
||||
* Constructor with a given strategy to convert a
|
||||
* {@link KeysetScrollPosition#getKeys() keyset} to and from a cursor.
|
||||
* @param keysetCursorStrategy the keyset cursor strategy
|
||||
*/
|
||||
public ScrollPositionCursorStrategy(CursorStrategy<Map<String, Object>> keysetCursorStrategy) {
|
||||
Assert.notNull(keysetCursorStrategy, "'keysetCursorStrategy' is required");
|
||||
@@ -80,7 +81,7 @@ public final class ScrollPositionCursorStrategy implements CursorStrategy<Scroll
|
||||
try {
|
||||
if (cursor.startsWith(OFFSET_PREFIX)) {
|
||||
long index = Long.parseLong(cursor.substring(2));
|
||||
return ScrollPosition.offset(index > 0 ? index : 0);
|
||||
return ScrollPosition.offset((index > 0) ? index : 0);
|
||||
}
|
||||
else if (cursor.startsWith(KEYSET_PREFIX)) {
|
||||
Map<String, Object> keys = this.keysetCursorStrategy.fromCursor(cursor.substring(2));
|
||||
|
||||
@@ -49,6 +49,9 @@ public final class ScrollSubrange extends Subrange<ScrollPosition> {
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
* @param pos the reference position, or {@code null} if not specified
|
||||
* @param count how many to return, or {@code null} if not specified
|
||||
* @param forward whether scroll forward (true) or backward (false)
|
||||
* @deprecated in favor of {@link #create}, to be removed in 1.3.
|
||||
*/
|
||||
@Deprecated(since = "1.2.4", forRemoval = true)
|
||||
@@ -111,7 +114,7 @@ public final class ScrollSubrange extends Subrange<ScrollPosition> {
|
||||
}
|
||||
else {
|
||||
// Advance back by 1 at least to item before position
|
||||
int advanceCount = (count != null ? count : 1);
|
||||
int advanceCount = (count != null) ? count : 1;
|
||||
if (position.getOffset() >= advanceCount) {
|
||||
position = position.advanceBy(-advanceCount);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public final class SliceConnectionAdapter
|
||||
/**
|
||||
* Constructor with the {@link CursorStrategy} to use to encode the
|
||||
* {@code ScrollPosition} of page items.
|
||||
* @param strategy the cursor strategy to use
|
||||
*/
|
||||
public SliceConnectionAdapter(CursorStrategy<ScrollPosition> strategy) {
|
||||
super(strategy);
|
||||
|
||||
@@ -32,6 +32,7 @@ public interface SortStrategy {
|
||||
/**
|
||||
* Return a {@link Sort} instance by extracting the sort information from
|
||||
* GraphQL arguments, or {@link Sort#unsorted()} otherwise.
|
||||
* @param environment the data fetching environment
|
||||
*/
|
||||
Sort extract(DataFetchingEnvironment environment);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.lang.Nullable;
|
||||
* Implementation of {@link GraphQlSource.Builder} that leaves it to subclasses
|
||||
* to initialize {@link GraphQLSchema}.
|
||||
*
|
||||
* @param <B> the builder type
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
@@ -90,8 +91,8 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
|
||||
|
||||
@Override
|
||||
public B configureGraphQl(Consumer<GraphQL.Builder> configurer) {
|
||||
this.graphQlConfigurer = (this.graphQlConfigurer != null ?
|
||||
this.graphQlConfigurer.andThen(configurer) : configurer);
|
||||
this.graphQlConfigurer = (this.graphQlConfigurer != null) ?
|
||||
this.graphQlConfigurer.andThen(configurer) : configurer;
|
||||
return self();
|
||||
}
|
||||
|
||||
@@ -147,13 +148,14 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
|
||||
visitorsToUse.add(ContextDataFetcherDecorator.createVisitor(this.subscriptionExceptionResolvers));
|
||||
|
||||
new SchemaTraverser().depthFirstFullSchema(visitorsToUse, schema, vars);
|
||||
return schema.transformWithoutTypes(builder -> builder.codeRegistry(outputCodeRegistry));
|
||||
return schema.transformWithoutTypes((builder) -> builder.codeRegistry(outputCodeRegistry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected method to apply the
|
||||
* {@link #configureGraphQl(Consumer) configured graphQlConfigurer}'s.
|
||||
* Subclasses can use this to customize {@link GraphQL.Builder} further.
|
||||
* @param builder the builder to be customized
|
||||
* @since 1.2.5
|
||||
*/
|
||||
protected void applyGraphQlConfigurers(GraphQL.Builder builder) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.execution;
|
||||
|
||||
import java.util.List;
|
||||
@@ -55,7 +56,6 @@ public interface BatchLoaderRegistry extends DataLoaderRegistrar {
|
||||
* {@code @SchemaMapping} handler methods can transparenly locate and
|
||||
* inject a {@code DataLoader<T>} argument based on the generic type
|
||||
* {@code <T>}.
|
||||
*
|
||||
* @param keyType the type of keys that will be used as input
|
||||
* @param valueType the type of value that will be returned as output
|
||||
* @param <K> the key type
|
||||
@@ -71,7 +71,6 @@ public interface BatchLoaderRegistry extends DataLoaderRegistrar {
|
||||
* <p><strong>Note:</strong> when this method is used, the parameter name
|
||||
* of a {@code DataLoader<T>} argument in a {@code @SchemaMapping} handler
|
||||
* method needs to match the name given here.
|
||||
*
|
||||
* @param name the name to use to register a {@code DataLoader}
|
||||
* @param <K> the type of keys that will be used as input
|
||||
* @param <V> the type of values that will be used as output
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.execution;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@@ -38,44 +38,43 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Mykyta Ivchenko
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.1
|
||||
*/
|
||||
class CompositeSubscriptionExceptionResolver implements SubscriptionExceptionResolver {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CompositeSubscriptionExceptionResolver.class);
|
||||
private static final Log logger = LogFactory.getLog(CompositeSubscriptionExceptionResolver.class);
|
||||
|
||||
private final List<SubscriptionExceptionResolver> resolvers;
|
||||
private final List<SubscriptionExceptionResolver> resolvers;
|
||||
|
||||
|
||||
CompositeSubscriptionExceptionResolver(List<SubscriptionExceptionResolver> resolvers) {
|
||||
Assert.notNull(resolvers, "'resolvers' is required");
|
||||
this.resolvers = resolvers;
|
||||
}
|
||||
CompositeSubscriptionExceptionResolver(List<SubscriptionExceptionResolver> resolvers) {
|
||||
Assert.notNull(resolvers, "'resolvers' is required");
|
||||
this.resolvers = resolvers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<List<GraphQLError>> resolveException(Throwable exception) {
|
||||
return Flux.fromIterable(this.resolvers)
|
||||
.flatMap(resolver -> resolver.resolveException(exception))
|
||||
.next()
|
||||
.onErrorResume(error -> Mono.just(handleResolverException(error, exception)))
|
||||
.defaultIfEmpty(createDefaultError());
|
||||
}
|
||||
@Override
|
||||
public Mono<List<GraphQLError>> resolveException(Throwable exception) {
|
||||
return Flux.fromIterable(this.resolvers)
|
||||
.flatMap((resolver) -> resolver.resolveException(exception))
|
||||
.next()
|
||||
.onErrorResume((error) -> Mono.just(handleResolverException(error, exception)))
|
||||
.defaultIfEmpty(createDefaultError());
|
||||
}
|
||||
|
||||
private List<GraphQLError> handleResolverException(
|
||||
Throwable resolverException, Throwable originalException) {
|
||||
private List<GraphQLError> handleResolverException(
|
||||
Throwable resolverException, Throwable originalException) {
|
||||
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failure while resolving " + originalException.getClass().getName(), resolverException);
|
||||
}
|
||||
return createDefaultError();
|
||||
}
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failure while resolving " + originalException.getClass().getName(), resolverException);
|
||||
}
|
||||
return createDefaultError();
|
||||
}
|
||||
|
||||
private List<GraphQLError> createDefaultError() {
|
||||
return Collections.singletonList(GraphqlErrorBuilder.newError()
|
||||
.message("Subscription error")
|
||||
.errorType(ErrorType.INTERNAL_ERROR)
|
||||
.build());
|
||||
}
|
||||
private List<GraphQLError> createDefaultError() {
|
||||
return Collections.singletonList(GraphqlErrorBuilder.newError()
|
||||
.message("Subscription error")
|
||||
.errorType(ErrorType.INTERNAL_ERROR)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.execution;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -67,7 +68,7 @@ public class ConnectionTypeDefinitionConfigurer implements TypeDefinitionConfigu
|
||||
.fieldDefinition(initFieldDefinition("endCursor", STRING_TYPE))
|
||||
.build());
|
||||
|
||||
typeNames.forEach(typeName -> {
|
||||
typeNames.forEach((typeName) -> {
|
||||
String connectionTypeName = typeName + "Connection";
|
||||
String edgeTypeName = typeName + "Edge";
|
||||
|
||||
@@ -90,19 +91,19 @@ public class ConnectionTypeDefinitionConfigurer implements TypeDefinitionConfigu
|
||||
return Stream.concat(
|
||||
registry.types().values().stream(),
|
||||
registry.objectTypeExtensions().values().stream().flatMap(Collection::stream))
|
||||
.filter(definition -> definition instanceof ImplementingTypeDefinition)
|
||||
.flatMap(definition -> {
|
||||
.filter((definition) -> definition instanceof ImplementingTypeDefinition)
|
||||
.flatMap((definition) -> {
|
||||
ImplementingTypeDefinition<?> typeDefinition = (ImplementingTypeDefinition<?>) definition;
|
||||
return typeDefinition.getFieldDefinitions().stream()
|
||||
.map(fieldDefinition -> {
|
||||
.map((fieldDefinition) -> {
|
||||
Type<?> type = fieldDefinition.getType();
|
||||
return (type instanceof NonNullType ? ((NonNullType) type).getType() : type);
|
||||
return (type instanceof NonNullType) ? ((NonNullType) type).getType() : type;
|
||||
})
|
||||
.filter(type -> type instanceof TypeName)
|
||||
.map(type -> ((TypeName) type).getName())
|
||||
.filter(name -> name.endsWith("Connection"))
|
||||
.filter(name -> registry.getType(name).isEmpty())
|
||||
.map(name -> name.substring(0, name.length() - "Connection".length()));
|
||||
.filter((type) -> type instanceof TypeName)
|
||||
.map((type) -> ((TypeName) type).getName())
|
||||
.filter((name) -> name.endsWith("Connection"))
|
||||
.filter((name) -> registry.getType(name).isEmpty())
|
||||
.map((name) -> name.substring(0, name.length() - "Connection".length()));
|
||||
})
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
@@ -88,13 +88,13 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
|
||||
|
||||
if (this.subscription) {
|
||||
Assert.state(value instanceof Publisher, "Expected Publisher for a subscription");
|
||||
Flux<?> flux = Flux.from((Publisher<?>) value).onErrorResume(exception -> {
|
||||
Flux<?> flux = Flux.from((Publisher<?>) value).onErrorResume((exception) -> {
|
||||
// Already handled, e.g. controller methods?
|
||||
if (exception instanceof SubscriptionPublisherException) {
|
||||
return Mono.error(exception);
|
||||
}
|
||||
return this.subscriptionExceptionResolver.resolveException(exception)
|
||||
.flatMap(errors -> Mono.error(new SubscriptionPublisherException(errors, exception)));
|
||||
.flatMap((errors) -> Mono.error(new SubscriptionPublisherException(errors, exception)));
|
||||
});
|
||||
return flux.contextWrite(snapshot::updateContext);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
|
||||
/**
|
||||
* Type visitor to apply {@link ContextDataFetcherDecorator}.
|
||||
*/
|
||||
private static class ContextTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
private static final class ContextTypeVisitor extends GraphQLTypeVisitorStub {
|
||||
|
||||
private final SubscriptionExceptionResolver exceptionResolver;
|
||||
|
||||
@@ -143,7 +143,7 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
|
||||
|
||||
if (applyDecorator(dataFetcher)) {
|
||||
boolean handlesSubscription = visitorHelper.isSubscriptionType(parent);
|
||||
dataFetcher = new ContextDataFetcherDecorator(dataFetcher, handlesSubscription, exceptionResolver);
|
||||
dataFetcher = new ContextDataFetcherDecorator(dataFetcher, handlesSubscription, this.exceptionResolver);
|
||||
codeRegistry.dataFetcher(parent, fieldDefinition, dataFetcher);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.execution;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -104,7 +105,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher
|
||||
.call();
|
||||
}
|
||||
catch (Exception ex2) {
|
||||
logger.warn("Failed to resolve " + exception, ex2);
|
||||
this.logger.warn("Failed to resolve " + exception, ex2);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -118,7 +119,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher
|
||||
@Nullable
|
||||
protected List<GraphQLError> resolveToMultipleErrors(Throwable ex, DataFetchingEnvironment env) {
|
||||
GraphQLError error = resolveToSingleError(ex, env);
|
||||
return (error != null ? Collections.singletonList(error) : null);
|
||||
return (error != null) ? Collections.singletonList(error) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +141,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher
|
||||
* @return the created instance
|
||||
* @deprecated as of 1.0.1, please use {@link DataFetcherExceptionResolver#forSingleError(BiFunction)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since = "1.0.1", forRemoval = true)
|
||||
public static DataFetcherExceptionResolverAdapter from(
|
||||
BiFunction<Throwable, DataFetchingEnvironment, GraphQLError> resolver) {
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.execution;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.execution;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -52,9 +53,9 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
|
||||
private final List<ReactorBatchLoader<?,?>> loaders = new ArrayList<>();
|
||||
private final List<ReactorBatchLoader<?, ?>> loaders = new ArrayList<>();
|
||||
|
||||
private final List<ReactorMappedBatchLoader<?,?>> mappedLoaders = new ArrayList<>();
|
||||
private final List<ReactorMappedBatchLoader<?, ?>> mappedLoaders = new ArrayList<>();
|
||||
|
||||
private final Supplier<DataLoaderOptions> defaultOptionsSupplier;
|
||||
|
||||
@@ -69,6 +70,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
/**
|
||||
* Constructor with a default {@link DataLoaderOptions} supplier to use as
|
||||
* a starting point for batch loader registrations.
|
||||
* @param defaultOptionsSupplier the supplier for data loader options
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public DefaultBatchLoaderRegistry(Supplier<DataLoaderOptions> defaultOptionsSupplier) {
|
||||
@@ -126,11 +128,11 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
@Nullable
|
||||
private Consumer<DataLoaderOptions> optionsConsumer;
|
||||
|
||||
public DefaultRegistrationSpec(Class<V> valueType) {
|
||||
DefaultRegistrationSpec(Class<V> valueType) {
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public DefaultRegistrationSpec(String name) {
|
||||
DefaultRegistrationSpec(String name) {
|
||||
this.name = name;
|
||||
this.valueType = null;
|
||||
}
|
||||
@@ -143,8 +145,8 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
|
||||
@Override
|
||||
public RegistrationSpec<K, V> withOptions(Consumer<DataLoaderOptions> optionsConsumer) {
|
||||
this.optionsConsumer = (this.optionsConsumer != null ?
|
||||
this.optionsConsumer.andThen(optionsConsumer) : optionsConsumer);
|
||||
this.optionsConsumer = (this.optionsConsumer != null) ?
|
||||
this.optionsConsumer.andThen(optionsConsumer) : optionsConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -177,7 +179,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
private Supplier<DataLoaderOptions> initOptionsSupplier() {
|
||||
|
||||
Supplier<DataLoaderOptions> optionsSupplier = () ->
|
||||
new DataLoaderOptions(this.options != null ?
|
||||
new DataLoaderOptions((this.options != null) ?
|
||||
this.options : DefaultBatchLoaderRegistry.this.defaultOptionsSupplier.get());
|
||||
|
||||
if (this.optionsConsumer == null) {
|
||||
@@ -197,7 +199,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
* {@link BatchLoaderWithContext} that delegates to a {@link Flux} batch
|
||||
* loading function and exposes Reactor context to it.
|
||||
*/
|
||||
private static class ReactorBatchLoader<K, V> implements BatchLoaderWithContext<K, V> {
|
||||
private static final class ReactorBatchLoader<K, V> implements BatchLoaderWithContext<K, V> {
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -214,11 +216,11 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
this.optionsSupplier = optionsSupplier;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public DataLoaderOptions getOptions() {
|
||||
DataLoaderOptions getOptions() {
|
||||
return this.optionsSupplier.get();
|
||||
}
|
||||
|
||||
@@ -246,7 +248,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
* {@link MappedBatchLoaderWithContext} that delegates to a {@link Mono}
|
||||
* batch loading function and exposes Reactor context to it.
|
||||
*/
|
||||
private static class ReactorMappedBatchLoader<K, V> implements MappedBatchLoaderWithContext<K, V> {
|
||||
private static final class ReactorMappedBatchLoader<K, V> implements MappedBatchLoaderWithContext<K, V> {
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -263,11 +265,11 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
|
||||
this.optionsSupplier = optionsSupplier;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public DataLoaderOptions getOptions() {
|
||||
DataLoaderOptions getOptions() {
|
||||
return this.optionsSupplier.get();
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService {
|
||||
(this.hasDataLoaderRegistrations ? registerDataLoaders(executionInput) : executionInput);
|
||||
|
||||
return Mono.fromFuture(this.graphQlSource.graphQl().executeAsync(updatedExecutionInput))
|
||||
.map(result -> new DefaultExecutionGraphQlResponse(updatedExecutionInput, result));
|
||||
.map((result) -> new DefaultExecutionGraphQlResponse(updatedExecutionInput, result));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService {
|
||||
if (existingRegistry == DataLoaderDispatcherInstrumentationState.EMPTY_DATALOADER_REGISTRY) {
|
||||
DataLoaderRegistry newRegistry = DataLoaderRegistry.newRegistry().build();
|
||||
applyDataLoaderRegistrars(newRegistry, graphQLContext);
|
||||
executionInput = executionInput.transform(builder -> builder.dataLoaderRegistry(newRegistry));
|
||||
executionInput = executionInput.transform((builder) -> builder.dataLoaderRegistry(newRegistry));
|
||||
}
|
||||
else {
|
||||
applyDataLoaderRegistrars(existingRegistry, graphQLContext);
|
||||
@@ -116,7 +116,7 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService {
|
||||
}
|
||||
|
||||
private void applyDataLoaderRegistrars(DataLoaderRegistry registry, GraphQLContext graphQLContext) {
|
||||
this.dataLoaderRegistrars.forEach(registrar -> registrar.registerDataLoaders(registry, graphQLContext));
|
||||
this.dataLoaderRegistrars.forEach((registrar) -> registrar.registerDataLoaders(registry, graphQLContext));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user