Skip to content

Commit d042b6d

Browse files
authored
Merge pull request opentripplanner#7200 from HSLdevcom/canceled-trips-mode-filter
Add modes filter and totalCount to the canceled trips query in the GTFS API
2 parents 4bea73e + a6abe7f commit d042b6d

21 files changed

Lines changed: 670 additions & 110 deletions

File tree

application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
3131
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
3232
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLQueryTypeStopsByRadiusArgs;
33+
import org.opentripplanner.apis.gtfs.mapping.CanceledTripsFilterMapper;
3334
import org.opentripplanner.apis.gtfs.mapping.routerequest.LegacyRouteRequestMapper;
3435
import org.opentripplanner.apis.gtfs.mapping.routerequest.RouteRequestMapper;
3536
import org.opentripplanner.apis.gtfs.support.filter.PatternByDateFilterUtil;
@@ -38,6 +39,8 @@
3839
import org.opentripplanner.ext.fares.model.FareRuleSet;
3940
import org.opentripplanner.ext.fares.service.gtfs.GtfsFaresService;
4041
import org.opentripplanner.ext.fares.service.gtfs.v1.DefaultFareService;
42+
import org.opentripplanner.framework.graphql.CountedConnection;
43+
import org.opentripplanner.framework.graphql.SimpleCountedListConnection;
4144
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
4245
import org.opentripplanner.gtfs.mapping.DirectionMapper;
4346
import org.opentripplanner.model.TripTimeOnDate;
@@ -818,10 +821,11 @@ public DataFetcher<Iterable<Trip>> trips() {
818821
}
819822

820823
@Override
821-
public DataFetcher<Connection<TripOnServiceDate>> canceledTrips() {
824+
public DataFetcher<CountedConnection<TripOnServiceDate>> canceledTrips() {
822825
return environment -> {
823-
var trips = getTransitService(environment).listCanceledTrips();
824-
return new SimpleListConnection<>(trips).get(environment);
826+
var request = CanceledTripsFilterMapper.mapToTripOnServiceDateRequest(environment);
827+
var trips = getTransitService(environment).findCanceledTrips(request);
828+
return new SimpleCountedListConnection<>(trips).get(environment);
825829
};
826830
}
827831

application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.opentripplanner.apis.gtfs.model.TripOccupancy;
3535
import org.opentripplanner.ext.fares.model.FareRuleSet;
3636
import org.opentripplanner.ext.ridehailing.model.RideEstimate;
37+
import org.opentripplanner.framework.graphql.CountedConnection;
3738
import org.opentripplanner.model.StopTimesInPattern;
3839
import org.opentripplanner.model.SystemNotice;
3940
import org.opentripplanner.model.TripTimeOnDate;
@@ -903,7 +904,7 @@ public interface GraphQLQueryType {
903904

904905
public DataFetcher<Iterable<VehicleRentalPlace>> bikeRentalStations();
905906

906-
public DataFetcher<Connection<TripOnServiceDate>> canceledTrips();
907+
public DataFetcher<CountedConnection<TripOnServiceDate>> canceledTrips();
907908

908909
public DataFetcher<Iterable<TripTimeOnDate>> cancelledTripTimes();
909910

@@ -1425,6 +1426,8 @@ public interface GraphQLTripOnServiceDateConnection {
14251426
public DataFetcher<Iterable<Edge<TripOnServiceDate>>> edges();
14261427

14271428
public DataFetcher<Object> pageInfo();
1429+
1430+
public DataFetcher<Integer> totalCount();
14281431
}
14291432

14301433
/**

application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,69 @@ public void setGraphQLWaitReluctance(Double waitReluctance) {
497497
}
498498
}
499499

500+
public static class GraphQLCanceledTripsFilterInput {
501+
502+
private List<GraphQLCanceledTripsFilterSelectInput> exclude;
503+
private List<GraphQLCanceledTripsFilterSelectInput> include;
504+
505+
public GraphQLCanceledTripsFilterInput(Map<String, Object> args) {
506+
if (args != null) {
507+
if (args.get("exclude") != null) {
508+
this.exclude = ((List<Map<String, Object>>) args.get("exclude")).stream()
509+
.map(o -> o == null ? null : new GraphQLCanceledTripsFilterSelectInput(o))
510+
.collect(Collectors.toList());
511+
}
512+
if (args.get("include") != null) {
513+
this.include = ((List<Map<String, Object>>) args.get("include")).stream()
514+
.map(o -> o == null ? null : new GraphQLCanceledTripsFilterSelectInput(o))
515+
.collect(Collectors.toList());
516+
}
517+
}
518+
}
519+
520+
public List<GraphQLCanceledTripsFilterSelectInput> getGraphQLExclude() {
521+
return this.exclude;
522+
}
523+
524+
public List<GraphQLCanceledTripsFilterSelectInput> getGraphQLInclude() {
525+
return this.include;
526+
}
527+
528+
public void setGraphQLExclude(List<GraphQLCanceledTripsFilterSelectInput> exclude) {
529+
this.exclude = exclude;
530+
}
531+
532+
public void setGraphQLInclude(List<GraphQLCanceledTripsFilterSelectInput> include) {
533+
this.include = include;
534+
}
535+
}
536+
537+
public static class GraphQLCanceledTripsFilterSelectInput {
538+
539+
private List<GraphQLTransitMode> modes;
540+
541+
public GraphQLCanceledTripsFilterSelectInput(Map<String, Object> args) {
542+
if (args != null) {
543+
if (args.get("modes") != null) {
544+
this.modes = ((List<Object>) args.get("modes")).stream()
545+
.map(item ->
546+
item instanceof GraphQLTransitMode ? item : GraphQLTransitMode.valueOf((String) item)
547+
)
548+
.map(GraphQLTransitMode.class::cast)
549+
.collect(Collectors.toList());
550+
}
551+
}
552+
}
553+
554+
public List<GraphQLTransitMode> getGraphQLModes() {
555+
return this.modes;
556+
}
557+
558+
public void setGraphQLModes(List<GraphQLTransitMode> modes) {
559+
this.modes = modes;
560+
}
561+
}
562+
500563
public static class GraphQLCarParkNameArgs {
501564

502565
private String language;
@@ -2590,13 +2653,17 @@ public static class GraphQLQueryTypeCanceledTripsArgs {
25902653

25912654
private String after;
25922655
private String before;
2656+
private GraphQLCanceledTripsFilterInput filters;
25932657
private Integer first;
25942658
private Integer last;
25952659

25962660
public GraphQLQueryTypeCanceledTripsArgs(Map<String, Object> args) {
25972661
if (args != null) {
25982662
this.after = (String) args.get("after");
25992663
this.before = (String) args.get("before");
2664+
this.filters = new GraphQLCanceledTripsFilterInput(
2665+
(Map<String, Object>) args.get("filters")
2666+
);
26002667
this.first = (Integer) args.get("first");
26012668
this.last = (Integer) args.get("last");
26022669
}
@@ -2610,6 +2677,10 @@ public String getGraphQLBefore() {
26102677
return this.before;
26112678
}
26122679

2680+
public GraphQLCanceledTripsFilterInput getGraphQLFilters() {
2681+
return this.filters;
2682+
}
2683+
26132684
public Integer getGraphQLFirst() {
26142685
return this.first;
26152686
}
@@ -2626,6 +2697,10 @@ public void setGraphQLBefore(String before) {
26262697
this.before = before;
26272698
}
26282699

2700+
public void setGraphQLFilters(GraphQLCanceledTripsFilterInput filters) {
2701+
this.filters = filters;
2702+
}
2703+
26292704
public void setGraphQLFirst(Integer first) {
26302705
this.first = first;
26312706
}

application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ config:
6161
Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate
6262
StopCall: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate
6363
TripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate
64-
TripOnServiceDateConnection: graphql.relay.Connection#Connection<TripOnServiceDate>
64+
TripOnServiceDateConnection: org.opentripplanner.framework.graphql.CountedConnection#CountedConnection<TripOnServiceDate>
6565
TripOnServiceDateEdge: graphql.relay.Edge#Edge<TripOnServiceDate>
6666
debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput
6767
DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package org.opentripplanner.apis.gtfs.mapping;
2+
3+
import graphql.schema.DataFetchingEnvironment;
4+
import java.util.List;
5+
import java.util.Set;
6+
import java.util.stream.Collectors;
7+
import java.util.stream.Stream;
8+
import javax.annotation.Nullable;
9+
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
10+
import org.opentripplanner.transit.api.model.FilterValues;
11+
import org.opentripplanner.transit.api.request.TripOnServiceDateRequest;
12+
import org.opentripplanner.transit.model.basic.TransitMode;
13+
import org.opentripplanner.utils.collection.CollectionUtils;
14+
15+
public class CanceledTripsFilterMapper {
16+
17+
public static TripOnServiceDateRequest mapToTripOnServiceDateRequest(
18+
DataFetchingEnvironment env
19+
) {
20+
var filter = new GraphQLTypes.GraphQLQueryTypeCanceledTripsArgs(
21+
env.getArguments()
22+
).getGraphQLFilters();
23+
var includes = filter.getGraphQLInclude();
24+
var excludes = filter.getGraphQLExclude();
25+
CollectionUtils.requireNullOrNonEmpty(includes, "filters.include");
26+
CollectionUtils.requireNullOrNonEmpty(excludes, "filters.exclude");
27+
var modesToInclude = CanceledTripsFilterMapper.toTransitModes(includes);
28+
var modesToExclude = CanceledTripsFilterMapper.toTransitModes(excludes);
29+
var modesToIncludeFilter = FilterValues.ofNullIsEverything("modesToInclude", modesToInclude);
30+
var modesToExcludeFilter = FilterValues.ofNullIsEverything("modesToExclude", modesToExclude);
31+
return TripOnServiceDateRequest.of()
32+
.withIncludeModes(modesToIncludeFilter)
33+
.withExcludeModes(modesToExcludeFilter)
34+
.build();
35+
}
36+
37+
@Nullable
38+
private static Set<TransitMode> toTransitModes(
39+
List<GraphQLTypes.GraphQLCanceledTripsFilterSelectInput> inputs
40+
) {
41+
if (inputs == null) {
42+
return null;
43+
}
44+
if (
45+
inputs
46+
.stream()
47+
.anyMatch(input -> input.getGraphQLModes() != null && input.getGraphQLModes().isEmpty())
48+
) {
49+
throw new IllegalArgumentException(
50+
"Mode filter must be either null or have at least one entry."
51+
);
52+
}
53+
return inputs
54+
.stream()
55+
.flatMap(include -> {
56+
var modes = include.getGraphQLModes();
57+
if (modes == null) {
58+
return Stream.of();
59+
}
60+
return include.getGraphQLModes().stream().map(TransitModeMapper::map);
61+
})
62+
.collect(Collectors.toSet());
63+
}
64+
}

application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyQuery.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,17 @@ public GraphQLFieldDefinition createQuery(GraphQLOutputType datedServiceJourneyT
122122
);
123123

124124
TripOnServiceDateRequestBuilder tripOnServiceDateRequestBuilder =
125-
TripOnServiceDateRequest.of(operatingDays)
126-
.withAgencies(authorities)
127-
.withRoutes(lines)
128-
.withServiceJourneys(serviceJourneys)
129-
.withReplacementFor(replacementFor);
125+
TripOnServiceDateRequest.of()
126+
.withIncludeServiceDates(operatingDays)
127+
.withIncludeAgencies(authorities)
128+
.withIncludeRoutes(lines)
129+
.withIncludeServiceJourneys(serviceJourneys)
130+
.withIncludeReplacementFor(replacementFor);
130131

131132
tripOnServiceDateRequestBuilder =
132-
tripOnServiceDateRequestBuilder.withNetexInternalPlanningCodes(privateCodes);
133+
tripOnServiceDateRequestBuilder.withIncludeNetexInternalPlanningCodes(privateCodes);
133134

134-
tripOnServiceDateRequestBuilder = tripOnServiceDateRequestBuilder.withAlterations(
135+
tripOnServiceDateRequestBuilder = tripOnServiceDateRequestBuilder.withIncludeAlterations(
135136
alterations
136137
);
137138

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.opentripplanner.framework.graphql;
2+
3+
import graphql.relay.Connection;
4+
5+
/**
6+
* This interface extends the standard GraphQL Connection interface with a method to get the total
7+
* number of items available in the underlying collection.
8+
*/
9+
public interface CountedConnection<T> extends Connection<T> {
10+
/**
11+
* The total number of items available in the underlying collection
12+
*/
13+
int getTotalCount();
14+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.opentripplanner.framework.graphql;
2+
3+
import graphql.relay.DefaultConnection;
4+
import graphql.relay.Edge;
5+
import graphql.relay.PageInfo;
6+
import java.util.List;
7+
import java.util.Objects;
8+
import org.opentripplanner.utils.tostring.ToStringBuilder;
9+
10+
/**
11+
* This class extends the {@link DefaultConnection} with a somewhat standard {@code totalCount}
12+
* field.
13+
*/
14+
public class DefaultCountedConnection<T>
15+
extends DefaultConnection<T>
16+
implements CountedConnection<T> {
17+
18+
private final Integer totalCount;
19+
20+
public DefaultCountedConnection(List<Edge<T>> edges, PageInfo pageInfo, Integer totalCount) {
21+
super(edges, pageInfo);
22+
this.totalCount = totalCount;
23+
}
24+
25+
@Override
26+
public int getTotalCount() {
27+
return totalCount;
28+
}
29+
30+
@Override
31+
public boolean equals(Object o) {
32+
if (this == o) {
33+
return true;
34+
}
35+
if (o == null || getClass() != o.getClass()) {
36+
return false;
37+
}
38+
DefaultCountedConnection<?> that = (DefaultCountedConnection<?>) o;
39+
40+
return (
41+
Objects.equals(getEdges(), that.getEdges()) &&
42+
Objects.equals(getPageInfo(), that.getPageInfo()) &&
43+
totalCount.equals(that.totalCount)
44+
);
45+
}
46+
47+
@Override
48+
public int hashCode() {
49+
return Objects.hash(getEdges(), getPageInfo(), totalCount);
50+
}
51+
52+
@Override
53+
public String toString() {
54+
return ToStringBuilder.of(getClass())
55+
.addColSize("edges", getEdges())
56+
.addObj("pageInfo", getPageInfo())
57+
.addNum("totalCount", totalCount)
58+
.toString();
59+
}
60+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.opentripplanner.framework.graphql;
2+
3+
import graphql.TrivialDataFetcher;
4+
import graphql.relay.SimpleListConnection;
5+
import graphql.schema.DataFetcher;
6+
import graphql.schema.DataFetchingEnvironment;
7+
import java.util.List;
8+
import java.util.Objects;
9+
10+
/**
11+
* This class is a version of the {@link SimpleListConnection} that returns a
12+
* {@link CountedConnection}.
13+
*/
14+
public class SimpleCountedListConnection<T>
15+
implements DataFetcher<CountedConnection<T>>, TrivialDataFetcher<CountedConnection<T>> {
16+
17+
private final List<T> data;
18+
19+
public SimpleCountedListConnection(List<T> data) {
20+
this.data = Objects.requireNonNull(data);
21+
}
22+
23+
@Override
24+
public CountedConnection<T> get(DataFetchingEnvironment environment) {
25+
var simpleListConnection = new SimpleListConnection<>(data);
26+
var connection = simpleListConnection.get(environment);
27+
return new DefaultCountedConnection<>(
28+
connection.getEdges(),
29+
connection.getPageInfo(),
30+
data.size()
31+
);
32+
}
33+
}

0 commit comments

Comments
 (0)