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

ISO-8601 date time for GTFS API itinerary responses #5660

Merged
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6d5be57
Add ISO date times to GraphQL API
leonardehrenfried Aug 8, 2023
67cf67e
Add scalar
leonardehrenfried Aug 8, 2023
5835324
Implement start/end for Itinerary
leonardehrenfried Aug 8, 2023
8a966c2
Use correct times for leg
leonardehrenfried Aug 8, 2023
ed766e6
Add iso time to Place
leonardehrenfried Aug 8, 2023
cb938be
Introduce consolidated times for legs
leonardehrenfried Aug 8, 2023
6e620e7
Update deprecation notice
leonardehrenfried Aug 8, 2023
d1a123a
Add ISO times to stop time
leonardehrenfried Aug 8, 2023
d5f4926
Rename LegTime to LegTimes
leonardehrenfried Nov 6, 2023
2b21e8f
Move deprecated methods to bottom
leonardehrenfried Nov 6, 2023
e12ecfb
Rename LegTime to LegTimes
leonardehrenfried Nov 7, 2023
644dbcb
Remove changes to StopTime
leonardehrenfried Nov 7, 2023
f69ca28
Rename to OffsetDateTime
leonardehrenfried Nov 7, 2023
f2e394f
Regenerate types, move deprecated fields to the bottom
leonardehrenfried Jan 6, 2024
dc7521e
Add tests for Duration
leonardehrenfried Jan 6, 2024
d319e5b
Add nullability check
leonardehrenfried Jan 6, 2024
67895be
Add documentation for OffsetDateTime
leonardehrenfried Feb 5, 2024
4f757da
Add test for intermediate places
leonardehrenfried Feb 5, 2024
980d620
Add test cases
leonardehrenfried Feb 5, 2024
dcaa04a
Add example for OffsetDateTime
leonardehrenfried Feb 5, 2024
691bdd4
Put realtime information into separate object
leonardehrenfried Feb 9, 2024
ce002b8
Fix combined leg
leonardehrenfried Feb 9, 2024
afb53e1
Fix tutorial docs
leonardehrenfried Feb 9, 2024
4197aed
Set transit delay
leonardehrenfried Feb 11, 2024
a534dd3
Use 'estimated' instead of 'realTime'
leonardehrenfried Feb 12, 2024
01b04e0
Use LegTimes for intermediate stops
leonardehrenfried Feb 12, 2024
b836c3b
Apply review feedback
leonardehrenfried Feb 13, 2024
bf7dc55
Implement OffsetDateTimeParser
leonardehrenfried Feb 14, 2024
049d023
Merge remote-tracking branch 'upstream/dev-2.x' into iso-datetime
leonardehrenfried Feb 15, 2024
6cb475c
Revert to 'LegTime'
leonardehrenfried Feb 19, 2024
2130da9
Rename fields
leonardehrenfried Feb 19, 2024
8b37735
Improve scalars
leonardehrenfried Feb 19, 2024
070be65
Merge remote-tracking branch 'upstream/dev-2.x' into iso-datetime
leonardehrenfried Feb 19, 2024
4d295da
Throw exception in scalar again
leonardehrenfried Feb 19, 2024
92c7acd
Switch order of arrival, departure
leonardehrenfried Feb 20, 2024
39f06dc
Add documentation
leonardehrenfried Feb 20, 2024
b4707ed
Add test for leg times
leonardehrenfried Feb 20, 2024
54c21a5
Fix flex leg and spelling
leonardehrenfried Feb 20, 2024
d99373d
Apply suggestions from code review
leonardehrenfried Feb 27, 2024
47d28f1
Update deprecation notices
leonardehrenfried Feb 27, 2024
337b100
Add newline
leonardehrenfried Feb 27, 2024
df0d65b
Always put minus as the first sign of a serialized negative duration
leonardehrenfried Mar 1, 2024
38f4c06
Merge remote-tracking branch 'upstream/dev-2.x' into iso-datetime
leonardehrenfried Mar 1, 2024
f723c25
Revert conversion to record
leonardehrenfried Mar 3, 2024
91989e2
Extract mapper for StopArrivals
leonardehrenfried Mar 3, 2024
84083b6
Separate tests several classes
leonardehrenfried Mar 3, 2024
10fac09
Make parser return throw an exception
leonardehrenfried Mar 3, 2024
009eb99
Remove arrival/departure in example
leonardehrenfried Mar 4, 2024
2bd2026
Add validation
leonardehrenfried Mar 4, 2024
a9406dc
Add import
leonardehrenfried Mar 4, 2024
964a3f7
Use uppercase for constants
leonardehrenfried Mar 4, 2024
f3a1858
Merge remote-tracking branch 'upstream/dev-2.x' into iso-datetime
leonardehrenfried Mar 4, 2024
2f763e9
Rename constant
leonardehrenfried Mar 5, 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
38 changes: 30 additions & 8 deletions docs/apis/GraphQL-Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,47 @@ Most people want to get routing results out of OTP, so lets see the query for th
},
]) {
itineraries {
startTime
endTime
start
end
legs {
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
mode
startTime
endTime
from {
name
lat
lon
departureTime
arrivalTime
arrival {
scheduledTime
estimated {
time
delay
}
}
departure {
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
scheduledTime
estimated {
time
delay
}
}
}
to {
name
lat
lon
departureTime
arrivalTime
arrival {
scheduledTime
estimated {
time
delay
}
}
departure {
scheduledTime
estimated {
time
delay
}
}
}
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
route {
gtfsId
Expand Down
3 changes: 2 additions & 1 deletion magidoc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ To learn how to deactivate it, read the
appTitle: 'OTP GTFS GraphQL API',
queryGenerationFactories: {
'Polyline': '<>',
'GeoJson': '<>'
'GeoJson': '<>',
'OffsetDateTime': '2024-02-05T18:04:23+01:00'
},
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ void flexTripInTransitMode() {

var intermediateStops = leg.getIntermediateStops();
assertEquals(1, intermediateStops.size());
assertEquals("zone_1", intermediateStops.get(0).place.stop.getId().getId());
assertEquals("zone_1", intermediateStops.get(0).place().stop.getId().getId());

EncodedPolyline legGeometry = EncodedPolyline.encode(leg.getLegGeometry());
assertThatPolylinesAreEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.opentripplanner.framework.collection.ListUtils;
import org.opentripplanner.model.fare.FareProductUse;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.model.plan.LegTime;
import org.opentripplanner.model.plan.Place;
import org.opentripplanner.model.plan.StopArrival;
import org.opentripplanner.model.plan.TransitLeg;
Expand Down Expand Up @@ -56,6 +57,16 @@ public Trip getTrip() {
return first.getTrip();
}

@Override
public LegTime start() {
return first.start();
}

@Override
public LegTime end() {
return second.end();
}

@Override
public ZonedDateTime getStartTime() {
return first.getStartTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.opentripplanner.model.PickDrop;
import org.opentripplanner.model.fare.FareProductUse;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.model.plan.LegTime;
import org.opentripplanner.model.plan.Place;
import org.opentripplanner.model.plan.StopArrival;
import org.opentripplanner.model.plan.TransitLeg;
Expand Down Expand Up @@ -84,6 +85,16 @@ public Accessibility getTripWheelchairAccessibility() {
return edge.getFlexTrip().getTrip().getWheelchairBoarding();
}

@Override
public LegTime start() {
return LegTime.ofStatic(startTime);
}

@Override
public LegTime end() {
return LegTime.ofStatic(endTime);
}

@Override
@Nonnull
public TransitMode getMode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ public List<ApiPlace> mapStopArrivals(Collection<StopArrival> domain) {

public ApiPlace mapStopArrival(StopArrival domain) {
return mapPlace(
domain.place,
domain.arrival,
domain.departure,
domain.stopPosInPattern,
domain.gtfsStopSequence
domain.place(),
domain.arrival().time(),
domain.departure().time(),
domain.stopPosInPattern(),
domain.gtfsStopSequence()
);
}

Expand Down
61 changes: 57 additions & 4 deletions src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
import graphql.schema.CoercingParseValueException;
import graphql.schema.CoercingSerializeException;
import graphql.schema.GraphQLScalarType;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import javax.annotation.Nonnull;
import org.locationtech.jts.geom.Geometry;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.framework.graphql.scalar.DurationScalarFactory;
import org.opentripplanner.framework.model.Grams;
import org.opentripplanner.framework.time.OffsetDateTimeParser;

public class GraphQLScalars {

private static final ObjectMapper geoJsonMapper = new ObjectMapper()
.registerModule(new JtsModule(GeometryUtils.getGeometryFactory()));
public static GraphQLScalarType durationScalar = DurationScalarFactory.createDurationScalar();

public static GraphQLScalarType polylineScalar = GraphQLScalarType
public static final GraphQLScalarType polylineScalar = GraphQLScalarType
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
.newScalar()
.name("Polyline")
.description(
Expand Down Expand Up @@ -50,7 +55,55 @@ public String parseLiteral(Object input) {
)
.build();

public static GraphQLScalarType geoJsonScalar = GraphQLScalarType
public static final GraphQLScalarType offsetDateTimeScalar = GraphQLScalarType
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
.newScalar()
.name("OffsetDateTime")
.coercing(
new Coercing<OffsetDateTime, String>() {
@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) {
return OffsetDateTimeParser.parseLeniently(cs).orElseThrow(() -> valueException(input));
}
throw valueException(input);
}

@Override
public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof StringValue sv) {
return OffsetDateTimeParser
.parseLeniently(sv.getValue())
.orElseThrow(CoercingParseLiteralException::new);
}
throw new CoercingParseLiteralException();
}

private static CoercingParseValueException valueException(Object input) {
return new CoercingParseValueException(
"Cannot parse %s into an OffsetDateTime.".formatted(input)
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
)
.build();

public static final GraphQLScalarType geoJsonScalar = GraphQLScalarType
.newScalar()
.name("GeoJson")
.description("Geographic data structures in JSON format. See: https://geojson.org/")
Expand Down Expand Up @@ -78,7 +131,7 @@ public Geometry parseLiteral(Object input) throws CoercingParseLiteralException
)
.build();

public static GraphQLScalarType graphQLIDScalar = GraphQLScalarType
public static final GraphQLScalarType graphQLIDScalar = GraphQLScalarType
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
.newScalar()
.name("ID")
.coercing(
Expand Down Expand Up @@ -118,7 +171,7 @@ public Relay.ResolvedGlobalId parseLiteral(Object input)
)
.build();

public static GraphQLScalarType gramsScalar = GraphQLScalarType
public static final GraphQLScalarType gramsScalar = GraphQLScalarType
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
.newScalar()
.name("Grams")
.coercing(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ protected static GraphQLSchema buildSchema() {
.scalar(GraphQLScalars.geoJsonScalar)
.scalar(GraphQLScalars.graphQLIDScalar)
.scalar(GraphQLScalars.gramsScalar)
.scalar(GraphQLScalars.offsetDateTimeScalar)
.scalar(ExtendedScalars.GraphQLLong)
.type("Node", type -> type.typeResolver(new NodeTypeResolver()))
.type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.time.OffsetDateTime;
import java.util.List;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.apis.gtfs.mapping.NumberMapper;
Expand Down Expand Up @@ -32,6 +33,12 @@ public DataFetcher<Double> elevationLost() {
return environment -> getSource(environment).getElevationLost();
}

@Override
public DataFetcher<OffsetDateTime> end() {
return environment -> getSource(environment).endTime().toOffsetDateTime();
}

@Deprecated
@Override
public DataFetcher<Long> endTime() {
return environment -> getSource(environment).endTime().toInstant().toEpochMilli();
Expand All @@ -57,6 +64,12 @@ public DataFetcher<Integer> numberOfTransfers() {
return environment -> getSource(environment).getNumberOfTransfers();
}

@Override
public DataFetcher<OffsetDateTime> start() {
return environment -> getSource(environment).startTime().toOffsetDateTime();
}

@Deprecated
@Override
public DataFetcher<Long> startTime() {
return environment -> getSource(environment).startTime().toInstant().toEpochMilli();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.opentripplanner.model.PickDrop;
import org.opentripplanner.model.fare.FareProductUse;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.model.plan.LegTime;
import org.opentripplanner.model.plan.ScheduledTransitLeg;
import org.opentripplanner.model.plan.StopArrival;
import org.opentripplanner.model.plan.StreetLeg;
Expand Down Expand Up @@ -78,6 +79,12 @@ public DataFetcher<Double> duration() {
}

@Override
public DataFetcher<LegTime> end() {
return environment -> getSource(environment).end();
}

@Override
@Deprecated
public DataFetcher<Long> endTime() {
return environment -> getSource(environment).getEndTime().toInstant().toEpochMilli();
}
Expand All @@ -93,8 +100,8 @@ public DataFetcher<StopArrival> from() {
Leg source = getSource(environment);
return new StopArrival(
source.getFrom(),
source.getStartTime(),
source.getStartTime(),
source.start(),
source.start(),
source.getBoardStopPosInPattern(),
source.getBoardingGtfsStopSequence()
);
Expand Down Expand Up @@ -137,7 +144,7 @@ public DataFetcher<Iterable<Object>> intermediateStops() {
}
return intermediateStops
.stream()
.map(intermediateStop -> intermediateStop.place.stop)
.map(intermediateStop -> intermediateStop.place().stop)
.filter(Objects::nonNull)
.collect(Collectors.toList());
};
Expand Down Expand Up @@ -216,6 +223,12 @@ public DataFetcher<String> serviceDate() {
}

@Override
public DataFetcher<LegTime> start() {
return environment -> getSource(environment).start();
}

@Override
@Deprecated
public DataFetcher<Long> startTime() {
return environment -> getSource(environment).getStartTime().toInstant().toEpochMilli();
}
Expand All @@ -231,8 +244,8 @@ public DataFetcher<StopArrival> to() {
Leg source = getSource(environment);
return new StopArrival(
source.getTo(),
source.getEndTime(),
source.getEndTime(),
source.end(),
source.end(),
source.getAlightStopPosInPattern(),
source.getAlightGtfsStopSequence()
);
Expand Down
Loading
Loading