Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plan query that follows the relay connection specification #5185

Merged
merged 174 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 171 commits
Commits
Show all changes
174 commits
Select commit Hold shift + click to select a range
c8f005f
Add initial version of planConnection schema
optionsome Jun 13, 2023
d0e1e6d
Update src/ext/resources/legacygraphqlapi/schema.graphqls
optionsome Aug 7, 2023
145897d
Update plan debug settings
optionsome Aug 19, 2023
aeebe38
Remove plan locations wrapper
optionsome Aug 19, 2023
1a5ae1b
Add wrapper for wheelchair preferences
optionsome Aug 19, 2023
f3a4219
Don't use abbreviations for lat/lon
optionsome Aug 19, 2023
910717a
Remove usage of timezone
optionsome Aug 19, 2023
d2f3af5
Use duration instead of long for debug information
optionsome Aug 19, 2023
00b165b
Use Duration instead of Int for bike switchTime
optionsome Aug 21, 2023
c26c09c
Update search window model
optionsome Aug 21, 2023
27e7fb1
Split place location
optionsome Aug 21, 2023
a83ffa4
Introduce DurationInSeconds
optionsome Aug 24, 2023
aa8455a
Remove extra quotes
optionsome Aug 24, 2023
31e5416
Simplify cost
optionsome Aug 28, 2023
8372e11
Merge realtime preferences into enum
optionsome Aug 28, 2023
c8635e3
Make slack parameter clearer
optionsome Aug 28, 2023
f58d5cd
Refactor datetime/search window input
optionsome Aug 31, 2023
c921b20
Rename penalty -> costPenalty
optionsome Aug 31, 2023
8075a67
Improve street modes
optionsome Aug 31, 2023
6a4e7c2
Split street modes into different enums
optionsome Sep 15, 2023
7f1e064
Remove pickup and dropoff from direct
optionsome Sep 15, 2023
c04708c
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Sep 15, 2023
371fe80
Add parking preferences
optionsome Sep 15, 2023
4618871
Add transfer number limits
optionsome Sep 15, 2023
5cb141a
Improve vehicle walking preferences
optionsome Sep 16, 2023
300e21e
Add rental time/cost parameters
optionsome Sep 16, 2023
8cd2a01
Use more scalars
optionsome Sep 17, 2023
c88f4fd
Split optimization types, rename them and rename safety
optionsome Sep 17, 2023
e608dc3
Improve readibility slightly
optionsome Sep 18, 2023
77bccc1
Use Input suffix on input types
optionsome Sep 18, 2023
2ad6ffa
Continue renaming and use an input type for timetable preferences
optionsome Sep 29, 2023
eade1f7
Remove some parameters that don't need to be exposed now
optionsome Oct 10, 2023
9099707
Use safety instead of friendliness again
optionsome Oct 10, 2023
1843124
Write schema documentation
optionsome Oct 19, 2023
e2fbdef
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Oct 20, 2023
907b156
Make new input type for unpreferred
optionsome Oct 20, 2023
b4addbd
Create own enum for input parameter so ANY mode can be added
optionsome Oct 20, 2023
c734775
Split access and egress modes and improve documentation
optionsome Oct 20, 2023
f53a208
Add another wrapper for location
optionsome Oct 20, 2023
befb250
Add documentation for locale
optionsome Oct 20, 2023
df9b225
Remove oneOf directive as it should now be in graphql-java
optionsome Oct 20, 2023
76159f7
Fix typo
optionsome Oct 20, 2023
8c3de15
Actually use input suffix in names
optionsome Oct 21, 2023
1885488
Fix examples
optionsome Oct 22, 2023
551cc31
Make locations mandatory
optionsome Oct 22, 2023
d75464b
Rename DateTimeWithOffset -> OffsetDateTime
optionsome Oct 23, 2023
154fa83
Improve documentation
optionsome Oct 23, 2023
a2c0391
Remove grace period, move search window fields to top level
optionsome Oct 23, 2023
fbcf032
Redesign stop as a location
optionsome Oct 23, 2023
373144f
Remove FeedScopedId scalar to leave room for unscoped ids
optionsome Oct 23, 2023
10b7cca
Do minor renaming
optionsome Oct 24, 2023
4bac478
Use Duration instead of DurationInSeconds and update reluctance doc
optionsome Nov 2, 2023
8c1450a
Use cost instead of cost penalty in naming
optionsome Nov 2, 2023
4beccfe
Remove CostFunction
optionsome Nov 2, 2023
e0b5e70
Add input type for destination vehicle policy
optionsome Nov 3, 2023
96a0b5f
Update src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls
optionsome Nov 6, 2023
fc12682
Remove outdated information about invariant and simplify field name
optionsome Nov 9, 2023
21839ec
Rename includeRealtimeUpdates -> excludeRealtimeUpdates
optionsome Nov 9, 2023
de7d9d5
Make stopLocationId mandatory
optionsome Nov 9, 2023
8f9b3c8
Update src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls
optionsome Nov 10, 2023
02d3fc1
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Nov 10, 2023
3a87b88
Simplify optimization input names
optionsome Nov 16, 2023
81bd78f
scooter/bicycleWalking -> walking
optionsome Nov 16, 2023
f1c0216
Rename transitConnectionRequired -> strict and set default
optionsome Nov 16, 2023
45c4128
Remove unpreferred from first version to leave room for improvement
optionsome Nov 16, 2023
5cc82db
Fix enum spelling
optionsome Nov 16, 2023
0bc718b
Include more itinerary filtering settings
optionsome Nov 16, 2023
9484f2d
Move rental and parking preferences under mode specific preferences
optionsome Nov 16, 2023
34c45b3
walking -> walk
optionsome Nov 16, 2023
1ef07da
Refactor modes
optionsome Nov 16, 2023
bd15a1c
Improve documentatio and replace WeightingFactor with Ratio
optionsome Nov 16, 2023
62fd8f7
Rename itinerary filter type/field and improve docs
optionsome Nov 23, 2023
41b553f
Make itinerary non null
optionsome Nov 23, 2023
c6164aa
Remove debug settings and move used search window to root
optionsome Nov 23, 2023
accad20
Remove locations type and move origin and destination to root
optionsome Nov 23, 2023
a882896
Create output plan location type and refactor coordinates
optionsome Dec 7, 2023
710b3ca
Field and type renaming
optionsome Dec 7, 2023
652f356
Apply some review feedback
optionsome Dec 12, 2023
8da714a
Update comments
optionsome Dec 12, 2023
80561b1
Add useRealtimeAvailability to rental preferences
optionsome Dec 12, 2023
9c765b8
Use more consistent spelling/naming
optionsome Dec 14, 2023
da7b8f4
Remove includeRealTimeAvailability
optionsome Dec 14, 2023
bb8f166
Make own destination policies for each rental type
optionsome Dec 14, 2023
667035d
Create own page info type for plan connection
optionsome Dec 19, 2023
b01c87c
Prefix type with Plan
optionsome Dec 19, 2023
dbac6a6
Make own vehicle walking input types for bicycle/scooter
optionsome Dec 19, 2023
71047d8
hopOnOrOffCost -> hopCost
optionsome Jan 2, 2024
7157ff8
Remove boardCost from scooter preferences
optionsome Jan 4, 2024
d3f8021
Only allow scooter rental as mode
optionsome Jan 4, 2024
e140b3e
Remove scooter walking
optionsome Jan 19, 2024
0a512fd
Apply suggestions from code review
optionsome Jan 22, 2024
1180b48
Remove old type
optionsome Jan 30, 2024
2813c8b
Rename hopCost/time -> mountDismountCost/Time
optionsome Jan 30, 2024
a570c56
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Feb 9, 2024
e2e6446
Add basic wiring
optionsome Feb 9, 2024
03f9f28
Add @oneOf back to schema and don't use it for optimization inputs
optionsome Feb 12, 2024
38d0020
Update magidoc and remove @oneOf definition from schema
optionsome Feb 12, 2024
95ac495
Bring back @oneOf directive
optionsome Feb 16, 2024
b178183
Add @specificiedBy to some scalars
optionsome Feb 16, 2024
1f112e8
Update OffsetDateTime scalar and implement coordinate scalar
optionsome Feb 19, 2024
8187ad5
Make origin and destination required
optionsome Feb 19, 2024
62a68ac
Rename old route request mapper
optionsome Feb 19, 2024
cd56144
Add basic implementation for planConnection
optionsome Feb 19, 2024
40405ea
Implement searchWindow, cursor and number of itineraries
optionsome Feb 19, 2024
b66adfc
Implement itinerary filters and ratio scalar
optionsome Feb 19, 2024
58bd1a4
Refactor so that correct exception is thrown
optionsome Feb 19, 2024
aff064c
Set default for number of itineraries
optionsome Feb 20, 2024
367b402
Remove static transit mode cost from schema
optionsome Feb 20, 2024
41fac36
Add basic implementation for modes arguments
optionsome Feb 20, 2024
0a3839b
Remove origin and destination from output
optionsome Feb 22, 2024
a025811
Remove strict stop location and some TODOs
optionsome Feb 22, 2024
0893557
Clarify numberOfItineraries
optionsome Feb 22, 2024
dd5880c
Update src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls
optionsome Feb 22, 2024
d2cc973
Implement accessibility preferences
optionsome Feb 22, 2024
72cbace
Make some costs Costs
optionsome Feb 23, 2024
9ffc1c0
Update @graphql-codegen/java version
optionsome Feb 23, 2024
bd30446
Use @oneOf for optimization input again
optionsome Feb 23, 2024
1b2a833
Implement cost scalar
optionsome Feb 23, 2024
66d61a4
Implement walk and bicycle preferences
optionsome Feb 23, 2024
2a1d43e
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Feb 26, 2024
d623a99
Implement scooter preferences
optionsome Feb 26, 2024
fe052e0
Temporarily use a non-stable graphql-java release
optionsome Feb 26, 2024
752f08a
Implement car preferences
optionsome Feb 26, 2024
fee5f9a
Add recommendation for using first/last
optionsome Feb 27, 2024
4cfa762
Implement transit preferences
optionsome Feb 27, 2024
33de97a
Validate access/egress/transfer
optionsome Feb 27, 2024
20e9550
Remove destination policy from car and implement it for scooter
optionsome Feb 27, 2024
fd29a03
Set arriveBy
optionsome Feb 27, 2024
6d23c57
Update test class name
optionsome Feb 28, 2024
a6bdc71
Mark old plan query as deprecated
optionsome Mar 1, 2024
9f3d64d
Add validation for costs, reluctances and durations
optionsome Mar 4, 2024
61ab6e0
Use uppercase for constants
optionsome Mar 4, 2024
d1fb070
Apply suggestions from code review
optionsome Mar 11, 2024
e5f1d23
Make new plan deprecated temporarily instead of the old one
optionsome Mar 11, 2024
539b3de
Use CollectionUtils isEmpty
optionsome Mar 11, 2024
ba77338
Don't override existing preferences if undefined in request
optionsome Mar 11, 2024
089a2ca
Fix copypaste mistake
optionsome Mar 11, 2024
3c9e438
Add tests for scalars
optionsome Mar 11, 2024
cf57eab
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Mar 14, 2024
5165c6b
Remove duplicate OffsetDateTime codegen
optionsome Mar 14, 2024
bd8e498
Remove some defaults that should be injected later on from code/conf
optionsome Mar 14, 2024
f969bd9
Refactor scalars and make min reluctance 0.1
optionsome Mar 14, 2024
bde1bd1
Add integration test
optionsome Mar 15, 2024
0232339
Add some tests for RouteRequestMapper
optionsome Mar 15, 2024
6f8d31b
Add test for transit modes
optionsome Mar 19, 2024
678e238
Fix allowedNetworks
optionsome Mar 21, 2024
36809be
Add tests for bike preferences
optionsome Mar 21, 2024
d52c5c4
Create own package for route request mapping
optionsome Mar 22, 2024
fef0e79
Split RouteRequestMapper
optionsome Mar 22, 2024
171a090
Split bicycle tests into own class
optionsome Mar 22, 2024
38a2fff
Remove duplication
optionsome Mar 23, 2024
7b4fcf2
Fix allowedNetworks for scooter and car as well
optionsome Mar 23, 2024
c358ba4
Rename walkSafetyFactor -> safetyFactor
optionsome Mar 23, 2024
427a7b2
Add tests for other street preferences
optionsome Mar 23, 2024
dd68ea1
Add transit and accesibility tests
optionsome Mar 23, 2024
c78619b
Add tests for utils and rename duration util method
optionsome Mar 25, 2024
be91f29
Implement access/egress/transfer/direct mode validation
optionsome Mar 25, 2024
451ea2e
Add more tests for street modes
optionsome Mar 25, 2024
50ff507
Better empty array arg handling
optionsome Mar 28, 2024
27e533b
Add tests
optionsome Apr 2, 2024
ffeadea
Fix rental availability use
optionsome Apr 11, 2024
c5f4f5b
Fix handling of Integer values in scalars
optionsome Apr 11, 2024
6df8377
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Apr 16, 2024
087c436
Update graphql-java-extended-scalars
optionsome Apr 16, 2024
18bbe02
Fix maxTransfers
optionsome Apr 16, 2024
cad90d3
Get rid of numberOfItineraries, use first instead
optionsome Apr 25, 2024
f8dc495
Minor code style change
optionsome Apr 25, 2024
ec8a81e
Use more sensible test method for testing size
optionsome Apr 25, 2024
22a9763
Refactor PlanPageInfo to be a class
optionsome Apr 26, 2024
e9d5d3d
Make eq/hc null safe
optionsome May 17, 2024
997b2fd
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome May 17, 2024
9e7e590
Merge remote-tracking branch 'upstream/dev-2.x' into plan-connection
optionsome Jun 3, 2024
d2a80c5
Update src/main/java/org/opentripplanner/apis/gtfs/model/PlanPageInfo…
optionsome Jun 3, 2024
e1552c2
Make equals more readable
optionsome Jun 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cibuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ jobs:

- name: Build GTFS GraphQL API documentation
run: |
npm install -g @magidoc/cli@4.0.0
npm install -g @magidoc/cli@4.1.4
magidoc generate

- name: Deploy compiled HTML to Github pages
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -852,12 +852,12 @@
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>21.5</version>
<version>22.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-extended-scalars</artifactId>
<version>21.0</version>
<version>22.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters;
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;
import graphql.language.Document;
import graphql.schema.GraphQLTypeUtil;
Expand All @@ -22,7 +21,6 @@
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
* Using this instrumentation we can precisely measure how queries and data fetchers are executed
Expand Down Expand Up @@ -107,21 +105,13 @@ public ExecutionStrategyInstrumentationContext beginExecutionStrategy(
) {
return new ExecutionStrategyInstrumentationContext() {
@Override
public void onDispatched(CompletableFuture<ExecutionResult> result) {}
public void onDispatched() {}

@Override
public void onCompleted(ExecutionResult result, Throwable t) {}
};
}

@Override
public InstrumentationContext<ExecutionResult> beginField(
InstrumentationFieldParameters parameters,
InstrumentationState state
) {
return noOp();
}

@Override
public InstrumentationContext<Object> beginFieldFetch(
InstrumentationFieldFetchParameters parameters,
Expand Down
287 changes: 275 additions & 12 deletions src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

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;
Expand All @@ -13,18 +15,19 @@
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.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 geoJsonMapper = ObjectMappers.geoJson();

public static GraphQLScalarType DURATION_SCALAR = DurationScalarFactory.createDurationScalar();
private static final ObjectMapper GEOJSON_MAPPER = ObjectMappers.geoJson();
public static final GraphQLScalarType DURATION_SCALAR = DurationScalarFactory.createDurationScalar();

public static final GraphQLScalarType POLYLINE_SCALAR = GraphQLScalarType
.newScalar()
Expand Down Expand Up @@ -111,6 +114,127 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce
)
.build();

public static final GraphQLScalarType COORDINATE_VALUE_SCALAR = GraphQLScalarType
.newScalar()
.name("CoordinateValue")
.coercing(
new Coercing<Double, Double>() {
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<Double> 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<Cost, Integer>() {
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<Cost> validateCost(int cost) {
if (cost >= 0 && cost <= MAX_COST) {
return Optional.of(Cost.costOfSeconds(cost));
}
return Optional.empty();
}
}
)
.build();

public static final GraphQLScalarType GEOJSON_SCALAR = GraphQLScalarType
.newScalar()
.name("GeoJson")
Expand All @@ -121,7 +245,7 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce
public JsonNode serialize(Object dataFetcherResult) throws CoercingSerializeException {
if (dataFetcherResult instanceof Geometry) {
var geom = (Geometry) dataFetcherResult;
return geoJsonMapper.valueToTree(geom);
return GEOJSON_MAPPER.valueToTree(geom);
}
return null;
}
Expand Down Expand Up @@ -195,20 +319,159 @@ public Double serialize(Object dataFetcherResult) throws CoercingSerializeExcept

@Override
public Grams parseValue(Object input) throws CoercingParseValueException {
if (input instanceof Double) {
var grams = (Double) input;
return new Grams(grams);
if (input instanceof Double doubleValue) {
return new Grams(doubleValue);
}
return null;
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 Double) {
var grams = (Double) input;
return new Grams(grams);
if (input instanceof FloatValue coordinate) {
return new Grams(coordinate.getValue().doubleValue());
}
return null;
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<Double, Double>() {
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<Double> 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<Double, Double>() {
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<Double> validateReluctance(double reluctance) {
if (reluctance >= MIN_Reluctance - 0.001 && reluctance <= MAX_Reluctance + 0.001) {
return Optional.of(reluctance);
}
return Optional.empty();
}
}
)
Expand Down
Loading
Loading