
org.opentripplanner.apis.transmodel.TransmodelGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
The newest version!
package org.opentripplanner.apis.transmodel;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.execution.ExecutionStrategy;
import graphql.execution.UnknownOperationException;
import graphql.execution.instrumentation.ChainedInstrumentation;
import graphql.execution.instrumentation.Instrumentation;
import graphql.schema.CoercingParseValueException;
import graphql.schema.GraphQLSchema;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import jakarta.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.opentripplanner.apis.support.graphql.LoggingDataFetcherExceptionHandler;
import org.opentripplanner.apis.transmodel.support.AbortOnUnprocessableRequestExecutionStrategy;
import org.opentripplanner.apis.transmodel.support.ExecutionResultMapper;
import org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.framework.application.OTPRequestTimeoutException;
import org.opentripplanner.framework.concurrent.OtpRequestThreadFactory;
import org.opentripplanner.framework.lang.ObjectUtils;
import org.opentripplanner.standalone.api.OtpServerRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class TransmodelGraph {
private static final Logger LOG = LoggerFactory.getLogger(TransmodelGraph.class);
private static final int MAX_ERROR_TO_RETURN = 25;
private final GraphQLSchema indexSchema;
final ExecutorService threadPool;
TransmodelGraph(GraphQLSchema schema) {
this.threadPool =
Executors.newCachedThreadPool(OtpRequestThreadFactory.of("transmodel-api-%d"));
this.indexSchema = schema;
}
Response executeGraphQL(
String query,
OtpServerRequestContext serverContext,
Map variables,
String operationName,
int maxNumberOfResultFields,
Iterable tracingTags
) {
try (var executionStrategy = new AbortOnUnprocessableRequestExecutionStrategy()) {
variables = ObjectUtils.ifNotNull(variables, new HashMap<>());
var instrumentation = createInstrumentation(maxNumberOfResultFields, tracingTags);
var transmodelRequestContext = createRequestContext(serverContext);
var executionInput = createExecutionInput(
query,
serverContext,
variables,
operationName,
transmodelRequestContext
);
var graphQL = createGraphQL(instrumentation, executionStrategy);
var result = graphQL.execute(executionInput);
result = limitMaxNumberOfErrors(result);
return ExecutionResultMapper.okResponse(result);
} catch (OTPRequestTimeoutException te) {
return ExecutionResultMapper.timeoutResponse();
} catch (ResponseTooLargeException rtle) {
return ExecutionResultMapper.tooLargeResponse(rtle.getMessage());
} catch (CoercingParseValueException | UnknownOperationException e) {
return ExecutionResultMapper.badRequestResponse(e.getMessage());
} catch (Exception systemError) {
LOG.error(systemError.getMessage(), systemError);
return ExecutionResultMapper.systemErrorResponse(systemError.getMessage());
}
}
private static Instrumentation createInstrumentation(
int maxNumberOfResultFields,
Iterable tracingTags
) {
Instrumentation instrumentation = new MaxFieldsInResultInstrumentation(maxNumberOfResultFields);
if (OTPFeature.ActuatorAPI.isOn()) {
instrumentation =
new ChainedInstrumentation(
new MicrometerGraphQLInstrumentation(Metrics.globalRegistry, tracingTags),
instrumentation
);
}
return instrumentation;
}
private static TransmodelRequestContext createRequestContext(
OtpServerRequestContext serverContext
) {
return new TransmodelRequestContext(
serverContext,
serverContext.routingService(),
serverContext.transitService()
);
}
private static ExecutionInput createExecutionInput(
String query,
OtpServerRequestContext serverContext,
Map variables,
String operationName,
TransmodelRequestContext transmodelRequestContext
) {
return ExecutionInput
.newExecutionInput()
.query(query)
.operationName(operationName)
.context(transmodelRequestContext)
.root(serverContext)
.variables(variables)
.build();
}
private GraphQL createGraphQL(
Instrumentation instrumentation,
ExecutionStrategy executionStrategy
) {
return GraphQL
.newGraphQL(indexSchema)
.instrumentation(instrumentation)
.queryExecutionStrategy(executionStrategy)
.defaultDataFetcherExceptionHandler(new LoggingDataFetcherExceptionHandler())
.build();
}
/**
* Reduce the number of errors returned down to limit
*/
private static ExecutionResult limitMaxNumberOfErrors(ExecutionResult result) {
var errors = result.getErrors();
if (errors.size() > MAX_ERROR_TO_RETURN) {
final var errorsShortList = errors.stream().limit(MAX_ERROR_TO_RETURN).toList();
result = result.transform(b -> b.errors(errorsShortList));
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy