All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphql_java_generator.client.QueryExecutorSpringReactiveImpl Maven / Gradle / Ivy

There is a newer version: 1.18
Show newest version
/**
 * 
 */
package com.graphql_java_generator.client;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.graphql_java_generator.annotation.RequestType;
import com.graphql_java_generator.client.request.AbstractGraphQLRequest;
import com.graphql_java_generator.client.response.JsonResponseWrapper;
import com.graphql_java_generator.exception.GraphQLRequestExecutionException;
import com.graphql_java_generator.spring.client.GraphQLAutoConfiguration;

import reactor.core.Disposable;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

/**
 * This is the default implementation for the {@link QueryExecutor} This implementation has been added in version
 * 1.12.
* It is loaded by the {@link GraphQLAutoConfiguration} Spring auto-configuration class. * * @since 1.12 * @author etienne-sf */ public class QueryExecutorSpringReactiveImpl implements QueryExecutor { /** Logger for this class */ private static Logger logger = LoggerFactory.getLogger(QueryExecutorSpringReactiveImpl.class); /** * A graphqlEndpoint Spring bean, of type String, must be provided, with the URL of the GraphQL endpoint, for * instance https://my.serveur.com/graphql */ String graphqlEndpoint; /** * If the subscription is on a different endpoint than the main GraphQL endpoint, thant you can define a * graphqlSubscriptionEndpoint Spring bean, of type String, with this specific URL, for instance * https://my.serveur.com/graphql/subscription. For instance, Java servers suffer from a limitation which * prevent to server both GET/POST HTTP verbs and WebSockets on the same URL.
* If no bean graphqlSubscriptionEndpoint Spring bean is defined, then the graphqlEndpoint URL is also * used for subscriptions (which is the standard case). */ String graphqlSubscriptionEndpoint; /** * The optional {@link ServerOAuth2AuthorizedClientExchangeFilterFunction} that manages the OAuth Authorization * header, on client side */ ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction; /** * The optional {@link OAuthTokenExtractor} that extracts the OAuth Authorization header, once the * {@link ServerOAuth2AuthorizedClientExchangeFilterFunction} has gotten it */ OAuthTokenExtractor oAuthTokenExtractor; /** * The Spring reactive {@link WebClient} that will execute the HTTP requests for GraphQL queries and mutations. */ WebClient webClient; /** * The Spring reactive {@link WebSocketClient} web socket client, that will execute HTTP requests to build the web * sockets, for GraphQL subscriptions.
* This is mandatory if the application latter calls subscription. It may be null otherwise. */ WebSocketClient webSocketClient; /** * The {@link ObjectMapper} that will read the json response, and map it to the correct java class, generated from * the GraphQL type defined in the source GraphQL schema */ ObjectMapper objectMapper = new ObjectMapper(); /** * This constructor may be called by Spring, once it has build a {@link WebClient} bean, or directly, in non Spring * applications. * * @param graphqlEndpoint * A graphqlEndpoint Spring bean, of type String, must be provided, with the URL of the GraphQL * endpoint, for instance https://my.serveur.com/graphql * @param graphqlSubscriptionEndpoint * If the subscription is on a different endpoint than the main GraphQL endpoint, thant you can define a * graphqlSubscriptionEndpoint Spring bean, of type String, with this specific URL, for instance * https://my.serveur.com/graphql/subscription. For instance, Java servers suffer from a * limitation which prevent to server both GET/POST HTTP verbs and WebSockets on the same URL.
* If no bean graphqlSubscriptionEndpoint Spring bean is defined, then the graphqlEndpoint * URL is also used for subscriptions (which is the standard case). * @param webClient * The Spring reactive {@link WebClient} that will execute the HTTP requests for GraphQL queries and * mutations. * @param webSocketClient * The Spring reactive {@link WebSocketClient} web socket client, that will execute HTTP requests to * build the web sockets, for GraphQL subscriptions.
* This is mandatory if the application latter calls subscription. It may be null otherwise. * @param serverOAuth2AuthorizedClientExchangeFilterFunction * The {@link ServerOAuth2AuthorizedClientExchangeFilterFunction} is responsible for getting OAuth token * from the OAuth authorization server. It is optional, and may be provided by the App's spring config. * If it is not provided, then there is no OAuth authentication on client side. If provided, then the * client uses it to provide the OAuth2 authorization token, when accessing the GraphQL resource server * for queries/mutations/subscriptions. * @param oAuthTokenExtractor * This class is responsible for extracting the OAuth token, once the * {@link ServerOAuth2AuthorizedClientExchangeFilterFunction} has done its job, and added the OAuth2 * token into the request, in the Authorization header. See the {@link OAuthTokenExtractor} doc for more * information. */ @Autowired public QueryExecutorSpringReactiveImpl(String graphqlEndpoint, // @Autowired(required = false) String graphqlSubscriptionEndpoint, // WebClient webClient, // @Autowired(required = false) WebSocketClient webSocketClient, @Autowired(required = false) ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction, @Autowired(required = false) OAuthTokenExtractor oAuthTokenExtractor) { this.graphqlEndpoint = graphqlEndpoint; this.graphqlSubscriptionEndpoint = graphqlSubscriptionEndpoint; this.webClient = webClient; this.webSocketClient = webSocketClient; this.serverOAuth2AuthorizedClientExchangeFilterFunction = serverOAuth2AuthorizedClientExchangeFilterFunction; this.oAuthTokenExtractor = oAuthTokenExtractor; } @Override public R execute(AbstractGraphQLRequest graphQLRequest, Map parameters, Class dataResponseType) throws GraphQLRequestExecutionException { if (graphQLRequest.getRequestType().equals(RequestType.subscription)) throw new GraphQLRequestExecutionException("This method may not be called for subscriptions"); String jsonRequest = graphQLRequest.buildRequest(parameters); try { logger.trace(GRAPHQL_MARKER, "Executing GraphQL request: {}", jsonRequest); JsonResponseWrapper responseJson = webClient// .post()// .contentType(MediaType.APPLICATION_JSON)// .body(Mono.just(jsonRequest), String.class)// .accept(MediaType.APPLICATION_JSON)// .retrieve()// .bodyToMono(JsonResponseWrapper.class)// .block(); return QueryExecutorImpl.parseDataFromGraphQLServerResponse(objectMapper, responseJson, dataResponseType); } catch (IOException e) { throw new GraphQLRequestExecutionException( "Error when executing query <" + jsonRequest + ">: " + e.getMessage(), e); } } @Override public SubscriptionClient execute(AbstractGraphQLRequest graphQLRequest, Map parameters, SubscriptionCallback subscriptionCallback, String subscriptionName, Class subscriptionType, Class messageType) throws GraphQLRequestExecutionException { // This method accepts only subscription at a time (no query and no mutation) if (!graphQLRequest.getRequestType().equals(RequestType.subscription)) throw new GraphQLRequestExecutionException("This method may be called only for subscriptions"); // Subscription may be subscribed only once at a time, as this method allows only one subscriptionCallback if (graphQLRequest.getSubscription().getFields().size() != 1) { throw new GraphQLRequestExecutionException( "This method may be called only for one subscription at a time, but there was " + graphQLRequest.getSubscription().getFields().size() + " subscriptions in this GraphQLRequest"); } // The subscription name must be the good one if (!graphQLRequest.getSubscription().getFields().get(0).getName().equals(subscriptionName)) { throw new GraphQLRequestExecutionException("The subscription provided in the GraphQLRequest is " + graphQLRequest.getSubscription().getFields().get(0).getName() + " but it should be " + subscriptionName); } // Is there an OAuth authentication to handle? HttpHeaders headers = new HttpHeaders(); if (serverOAuth2AuthorizedClientExchangeFilterFunction != null && oAuthTokenExtractor != null) { String authorizationHeaderValue = oAuthTokenExtractor.getAuthorizationHeaderValue(); logger.debug("Got this OAuth token (authorization header value): {}", authorizationHeaderValue); headers.add(OAuthTokenExtractor.AUTHORIZATION_HEADER_NAME, authorizationHeaderValue); } else { logger.debug( "No serverOAuth2AuthorizedClientExchangeFilterFunction or no oAuthTokenExtractor where provided. No OAuth token is provided."); } String request = graphQLRequest.buildRequest(parameters); logger.debug(GRAPHQL_MARKER, "Executing GraphQL subscription '{}' with request {}", subscriptionName, request); // Let's create and start the Web Socket GraphQLReactiveWebSocketHandler webSocketHandler = new GraphQLReactiveWebSocketHandler<>(request, subscriptionName, subscriptionCallback, subscriptionType, messageType); logger.trace(GRAPHQL_MARKER, "Before execution of GraphQL subscription '{}' with request {}", subscriptionName, request); Disposable disposable = webSocketClient.execute(getWebSocketURI(), headers, webSocketHandler) .subscribeOn(Schedulers.single())// Let's have a dedicated thread .subscribe(); logger.trace(GRAPHQL_MARKER, "After execution of GraphQL subscription '{}' with request {}", subscriptionName, request); return new SubscriptionClientReactiveImpl(disposable, webSocketHandler.getSession()); } /** * Retrieves the URI for the Web Socket, based on the GraphQL endpoint that has been given to the Constructor * * @return * @throws GraphQLRequestExecutionException */ public URI getWebSocketURI() throws GraphQLRequestExecutionException { String endpoint = (graphqlSubscriptionEndpoint != null) ? graphqlSubscriptionEndpoint : graphqlEndpoint; if (endpoint.startsWith("http:") || endpoint.startsWith("https:")) { // We'll use the ws or the wss protocol. Let's just replace http by ws for that try { return new URI("ws" + endpoint.substring(4)); } catch (URISyntaxException e) { throw new GraphQLRequestExecutionException( "Error when trying to determine the Web Socket endpoint for GraphQL endpoint " + endpoint, e); } } throw new GraphQLRequestExecutionException( "non managed protocol for endpoint " + endpoint + ". This method manages only http and https"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy