
org.opentripplanner.apis.gtfs.GraphQLScalars 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.gtfs;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import graphql.language.FloatValue;
import graphql.language.IntValue;
import graphql.language.StringValue;
import graphql.relay.Relay;
import graphql.schema.Coercing;
import graphql.schema.CoercingParseLiteralException;
import graphql.schema.CoercingParseValueException;
import graphql.schema.CoercingSerializeException;
import graphql.schema.GraphQLScalarType;
import java.text.ParseException;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import javax.annotation.Nonnull;
import org.locationtech.jts.geom.Geometry;
import org.opentripplanner.framework.graphql.scalar.DateScalarFactory;
import org.opentripplanner.framework.graphql.scalar.DurationScalarFactory;
import org.opentripplanner.framework.json.ObjectMappers;
import org.opentripplanner.framework.model.Cost;
import org.opentripplanner.framework.model.Grams;
import org.opentripplanner.framework.time.OffsetDateTimeParser;
public class GraphQLScalars {
private static final ObjectMapper GEOJSON_MAPPER = ObjectMappers.geoJson();
public static final GraphQLScalarType DURATION_SCALAR = DurationScalarFactory.createDurationScalar();
public static final GraphQLScalarType POLYLINE_SCALAR = GraphQLScalarType
.newScalar()
.name("Polyline")
.description(
"List of coordinates in an encoded polyline format (see https://developers.google.com/maps/documentation/utilities/polylinealgorithm). The value appears in JSON as a string."
)
.coercing(
new Coercing() {
@Override
public String serialize(Object input) {
return input == null ? null : input.toString();
}
@Override
public String parseValue(Object input) {
return serialize(input);
}
@Override
public String parseLiteral(Object input) {
if (!(input instanceof StringValue)) {
return null;
}
return ((StringValue) input).getValue();
}
}
)
.build();
public static final GraphQLScalarType OFFSET_DATETIME_SCALAR = GraphQLScalarType
.newScalar()
.name("OffsetDateTime")
.coercing(
new Coercing() {
@Override
public String serialize(@Nonnull Object dataFetcherResult)
throws CoercingSerializeException {
if (dataFetcherResult instanceof ZonedDateTime zdt) {
return zdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
} else if (dataFetcherResult instanceof OffsetDateTime odt) {
return odt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
} else {
throw new CoercingSerializeException(
"Cannot serialize object of class %s".formatted(
dataFetcherResult.getClass().getSimpleName()
)
);
}
}
@Override
public OffsetDateTime parseValue(Object input) throws CoercingParseValueException {
if (input instanceof CharSequence cs) {
try {
return OffsetDateTimeParser.parseLeniently(cs);
} catch (ParseException e) {
int errorOffset = e.getErrorOffset();
throw new CoercingParseValueException(
"Cannot parse %s into an OffsetDateTime. Error at character index %s".formatted(
input,
errorOffset
)
);
}
}
throw new CoercingParseValueException(
"Cannot parse %s into an OffsetDateTime. Must be a string."
);
}
@Override
public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof StringValue sv) {
try {
return OffsetDateTimeParser.parseLeniently(sv.getValue());
} catch (ParseException e) {
throw new CoercingSerializeException();
}
}
throw new CoercingParseLiteralException();
}
}
)
.build();
public static final GraphQLScalarType COORDINATE_VALUE_SCALAR = GraphQLScalarType
.newScalar()
.name("CoordinateValue")
.coercing(
new Coercing() {
private static final String VALIDATION_ERROR_MESSAGE = "Not a valid WGS84 coordinate value";
@Override
public Double serialize(@Nonnull Object dataFetcherResult)
throws CoercingSerializeException {
if (dataFetcherResult instanceof Double doubleValue) {
return doubleValue;
} else if (dataFetcherResult instanceof Float floatValue) {
return floatValue.doubleValue();
} else {
throw new CoercingSerializeException(
"Cannot serialize object of class %s as a coordinate number".formatted(
dataFetcherResult.getClass().getSimpleName()
)
);
}
}
@Override
public Double parseValue(Object input) throws CoercingParseValueException {
if (input instanceof Double doubleValue) {
return validateCoordinate(doubleValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
if (input instanceof Integer intValue) {
return validateCoordinate(intValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseValueException(
"Expected a number, got %s %s".formatted(input.getClass().getSimpleName(), input)
);
}
@Override
public Double parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof FloatValue coordinate) {
return validateCoordinate(coordinate.getValue().doubleValue())
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
if (input instanceof IntValue coordinate) {
return validateCoordinate(coordinate.getValue().doubleValue())
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseLiteralException(
"Expected a number, got: " + input.getClass().getSimpleName()
);
}
private static Optional validateCoordinate(double coordinate) {
if (coordinate >= -180.001 && coordinate <= 180.001) {
return Optional.of(coordinate);
}
return Optional.empty();
}
}
)
.build();
public static final GraphQLScalarType COST_SCALAR = GraphQLScalarType
.newScalar()
.name("Cost")
.coercing(
new Coercing() {
private static final int MAX_COST = 1000000;
private static final String VALIDATION_ERROR_MESSAGE =
"Cost cannot be negative or greater than %d".formatted(MAX_COST);
@Override
public Integer serialize(@Nonnull Object dataFetcherResult)
throws CoercingSerializeException {
if (dataFetcherResult instanceof Integer intValue) {
return intValue;
} else if (dataFetcherResult instanceof Cost costValue) {
return costValue.toSeconds();
} else {
throw new CoercingSerializeException(
"Cannot serialize object of class %s as a cost".formatted(
dataFetcherResult.getClass().getSimpleName()
)
);
}
}
@Override
public Cost parseValue(Object input) throws CoercingParseValueException {
if (input instanceof Integer intValue) {
return validateCost(intValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseValueException(
"Expected an integer, got %s %s".formatted(input.getClass().getSimpleName(), input)
);
}
@Override
public Cost parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof IntValue intValue) {
var value = intValue.getValue().intValue();
return validateCost(value)
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseLiteralException(
"Expected an integer, got: " + input.getClass().getSimpleName()
);
}
private static Optional validateCost(int cost) {
if (cost >= 0 && cost <= MAX_COST) {
return Optional.of(Cost.costOfSeconds(cost));
}
return Optional.empty();
}
}
)
.build();
public static final GraphQLScalarType LOCAL_DATE_SCALAR = DateScalarFactory.createGtfsDateScalar();
public static final GraphQLScalarType GEOJSON_SCALAR = GraphQLScalarType
.newScalar()
.name("GeoJson")
.description("Geographic data structures in JSON format. See: https://geojson.org/")
.coercing(
new Coercing() {
@Override
public JsonNode serialize(Object dataFetcherResult) throws CoercingSerializeException {
if (dataFetcherResult instanceof Geometry) {
var geom = (Geometry) dataFetcherResult;
return GEOJSON_MAPPER.valueToTree(geom);
}
return null;
}
@Override
public Geometry parseValue(Object input) throws CoercingParseValueException {
return null;
}
@Override
public Geometry parseLiteral(Object input) throws CoercingParseLiteralException {
return null;
}
}
)
.build();
public static final GraphQLScalarType GRAPHQL_ID_SCALAR = GraphQLScalarType
.newScalar()
.name("ID")
.coercing(
new Coercing() {
@Override
public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
if (dataFetcherResult instanceof Relay.ResolvedGlobalId) {
Relay.ResolvedGlobalId globalId = (Relay.ResolvedGlobalId) dataFetcherResult;
return new Relay().toGlobalId(globalId.getType(), globalId.getId());
}
throw new CoercingSerializeException(
"Unknown type " + dataFetcherResult.getClass().getSimpleName()
);
}
@Override
public Relay.ResolvedGlobalId parseValue(Object input) throws CoercingParseValueException {
if (input instanceof String) {
return new Relay().fromGlobalId((String) input);
}
throw new CoercingParseValueException(
"Unexpected type " + input.getClass().getSimpleName()
);
}
@Override
public Relay.ResolvedGlobalId parseLiteral(Object input)
throws CoercingParseLiteralException {
if (input instanceof StringValue) {
return new Relay().fromGlobalId(((StringValue) input).getValue());
}
throw new CoercingParseLiteralException(
"Unexpected type " + input.getClass().getSimpleName()
);
}
}
)
.build();
public static final GraphQLScalarType GRAMS_SCALAR = GraphQLScalarType
.newScalar()
.name("Grams")
.coercing(
new Coercing() {
@Override
public Double serialize(Object dataFetcherResult) throws CoercingSerializeException {
if (dataFetcherResult instanceof Grams) {
var grams = (Grams) dataFetcherResult;
return Double.valueOf(grams.asDouble());
}
return null;
}
@Override
public Grams parseValue(Object input) throws CoercingParseValueException {
if (input instanceof Double doubleValue) {
return new Grams(doubleValue);
}
if (input instanceof Integer intValue) {
return new Grams(intValue);
}
throw new CoercingParseValueException(
"Expected a number, got %s %s".formatted(input.getClass().getSimpleName(), input)
);
}
@Override
public Grams parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof FloatValue coordinate) {
return new Grams(coordinate.getValue().doubleValue());
}
if (input instanceof IntValue coordinate) {
return new Grams(coordinate.getValue().doubleValue());
}
throw new CoercingParseLiteralException(
"Expected a number, got: " + input.getClass().getSimpleName()
);
}
}
)
.build();
public static final GraphQLScalarType RATIO_SCALAR = GraphQLScalarType
.newScalar()
.name("Ratio")
.coercing(
new Coercing() {
private static final String VALIDATION_ERROR_MESSAGE =
"Value is under 0 or greater than 1.";
@Override
public Double serialize(@Nonnull Object dataFetcherResult)
throws CoercingSerializeException {
var validationException = new CoercingSerializeException(VALIDATION_ERROR_MESSAGE);
if (dataFetcherResult instanceof Double doubleValue) {
return validateRatio(doubleValue).orElseThrow(() -> validationException);
} else if (dataFetcherResult instanceof Float floatValue) {
return validateRatio(floatValue.doubleValue()).orElseThrow(() -> validationException);
} else {
throw new CoercingSerializeException(
"Cannot serialize object of class %s as a ratio".formatted(
dataFetcherResult.getClass().getSimpleName()
)
);
}
}
@Override
public Double parseValue(Object input) throws CoercingParseValueException {
if (input instanceof Double doubleValue) {
return validateRatio(doubleValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
if (input instanceof Integer intValue) {
return validateRatio(intValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseValueException(
"Expected a number, got %s %s".formatted(input.getClass().getSimpleName(), input)
);
}
@Override
public Double parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof FloatValue ratio) {
return validateRatio(ratio.getValue().doubleValue())
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
if (input instanceof IntValue ratio) {
return validateRatio(ratio.getValue().doubleValue())
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseLiteralException(
"Expected a number, got: " + input.getClass().getSimpleName()
);
}
private static Optional validateRatio(double ratio) {
if (ratio >= -0.001 && ratio <= 1.001) {
return Optional.of(ratio);
}
return Optional.empty();
}
}
)
.build();
public static final GraphQLScalarType RELUCTANCE_SCALAR = GraphQLScalarType
.newScalar()
.name("Reluctance")
.coercing(
new Coercing() {
private static final double MIN_Reluctance = 0.1;
private static final double MAX_Reluctance = 100000;
private static final String VALIDATION_ERROR_MESSAGE =
"Reluctance needs to be between %s and %s".formatted(MIN_Reluctance, MAX_Reluctance);
@Override
public Double serialize(@Nonnull Object dataFetcherResult)
throws CoercingSerializeException {
if (dataFetcherResult instanceof Double doubleValue) {
return doubleValue;
} else if (dataFetcherResult instanceof Float floatValue) {
return floatValue.doubleValue();
} else {
throw new CoercingSerializeException(
"Cannot serialize object of class %s as a reluctance".formatted(
dataFetcherResult.getClass().getSimpleName()
)
);
}
}
@Override
public Double parseValue(Object input) throws CoercingParseValueException {
if (input instanceof Double doubleValue) {
return validateReluctance(doubleValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
if (input instanceof Integer intValue) {
return validateReluctance(intValue)
.orElseThrow(() -> new CoercingParseValueException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseValueException(
"Expected a number, got %s %s".formatted(input.getClass().getSimpleName(), input)
);
}
@Override
public Double parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof FloatValue reluctance) {
return validateReluctance(reluctance.getValue().doubleValue())
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
if (input instanceof IntValue reluctance) {
return validateReluctance(reluctance.getValue().doubleValue())
.orElseThrow(() -> new CoercingParseLiteralException(VALIDATION_ERROR_MESSAGE));
}
throw new CoercingParseLiteralException(
"Expected a number, got: " + input.getClass().getSimpleName()
);
}
private static Optional validateReluctance(double reluctance) {
if (reluctance >= MIN_Reluctance - 0.001 && reluctance <= MAX_Reluctance + 0.001) {
return Optional.of(reluctance);
}
return Optional.empty();
}
}
)
.build();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy