Skip to content

Commit 302547e

Browse files
committed
Revert "Revert "Revert "Revert new filter API temporarily"""
This reverts commit afb98cd.
1 parent 193c651 commit 302547e

12 files changed

Lines changed: 561 additions & 12 deletions

File tree

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5110,6 +5110,72 @@ public void setGraphQLSlack(java.time.Duration slack) {
51105110
}
51115111
}
51125112

5113+
public static class GraphQLTransitFilterInput {
5114+
5115+
private List<GraphQLTransitFilterSelectInput> exclude;
5116+
private List<GraphQLTransitFilterSelectInput> include;
5117+
5118+
public GraphQLTransitFilterInput(Map<String, Object> args) {
5119+
if (args != null) {
5120+
if (args.get("exclude") != null) {
5121+
this.exclude = ((List<Map<String, Object>>) args.get("exclude")).stream()
5122+
.map(o -> o == null ? null : new GraphQLTransitFilterSelectInput(o))
5123+
.collect(Collectors.toList());
5124+
}
5125+
if (args.get("include") != null) {
5126+
this.include = ((List<Map<String, Object>>) args.get("include")).stream()
5127+
.map(o -> o == null ? null : new GraphQLTransitFilterSelectInput(o))
5128+
.collect(Collectors.toList());
5129+
}
5130+
}
5131+
}
5132+
5133+
public List<GraphQLTransitFilterSelectInput> getGraphQLExclude() {
5134+
return this.exclude;
5135+
}
5136+
5137+
public List<GraphQLTransitFilterSelectInput> getGraphQLInclude() {
5138+
return this.include;
5139+
}
5140+
5141+
public void setGraphQLExclude(List<GraphQLTransitFilterSelectInput> exclude) {
5142+
this.exclude = exclude;
5143+
}
5144+
5145+
public void setGraphQLInclude(List<GraphQLTransitFilterSelectInput> include) {
5146+
this.include = include;
5147+
}
5148+
}
5149+
5150+
public static class GraphQLTransitFilterSelectInput {
5151+
5152+
private List<String> agencies;
5153+
private List<String> routes;
5154+
5155+
public GraphQLTransitFilterSelectInput(Map<String, Object> args) {
5156+
if (args != null) {
5157+
this.agencies = (List<String>) args.get("agencies");
5158+
this.routes = (List<String>) args.get("routes");
5159+
}
5160+
}
5161+
5162+
public List<String> getGraphQLAgencies() {
5163+
return this.agencies;
5164+
}
5165+
5166+
public List<String> getGraphQLRoutes() {
5167+
return this.routes;
5168+
}
5169+
5170+
public void setGraphQLAgencies(List<String> agencies) {
5171+
this.agencies = agencies;
5172+
}
5173+
5174+
public void setGraphQLRoutes(List<String> routes) {
5175+
this.routes = routes;
5176+
}
5177+
}
5178+
51135179
/**
51145180
* Transit modes include modes that are used within organized transportation networks
51155181
* run by public transportation authorities, taxi companies etc.
@@ -5155,13 +5221,19 @@ public static class GraphQLTransitPreferencesInput {
51555221

51565222
private GraphQLAlightPreferencesInput alight;
51575223
private GraphQLBoardPreferencesInput board;
5224+
private List<GraphQLTransitFilterInput> filters;
51585225
private GraphQLTimetablePreferencesInput timetable;
51595226
private GraphQLTransferPreferencesInput transfer;
51605227

51615228
public GraphQLTransitPreferencesInput(Map<String, Object> args) {
51625229
if (args != null) {
51635230
this.alight = new GraphQLAlightPreferencesInput((Map<String, Object>) args.get("alight"));
51645231
this.board = new GraphQLBoardPreferencesInput((Map<String, Object>) args.get("board"));
5232+
if (args.get("filters") != null) {
5233+
this.filters = ((List<Map<String, Object>>) args.get("filters")).stream()
5234+
.map(o -> o == null ? null : new GraphQLTransitFilterInput(o))
5235+
.collect(Collectors.toList());
5236+
}
51655237
this.timetable = new GraphQLTimetablePreferencesInput(
51665238
(Map<String, Object>) args.get("timetable")
51675239
);
@@ -5179,6 +5251,10 @@ public GraphQLBoardPreferencesInput getGraphQLBoard() {
51795251
return this.board;
51805252
}
51815253

5254+
public List<GraphQLTransitFilterInput> getGraphQLFilters() {
5255+
return this.filters;
5256+
}
5257+
51825258
public GraphQLTimetablePreferencesInput getGraphQLTimetable() {
51835259
return this.timetable;
51845260
}
@@ -5195,6 +5271,10 @@ public void setGraphQLBoard(GraphQLBoardPreferencesInput board) {
51955271
this.board = board;
51965272
}
51975273

5274+
public void setGraphQLFilters(List<GraphQLTransitFilterInput> filters) {
5275+
this.filters = filters;
5276+
}
5277+
51985278
public void setGraphQLTimetable(GraphQLTimetablePreferencesInput timetable) {
51995279
this.timetable = timetable;
52005280
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.opentripplanner.apis.gtfs.mapping.routerequest;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import org.opentripplanner.apis.gtfs.GraphQLUtils;
6+
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
7+
import org.opentripplanner.routing.api.request.request.filter.SelectRequest;
8+
import org.opentripplanner.routing.api.request.request.filter.TransitFilter;
9+
import org.opentripplanner.routing.api.request.request.filter.TransitFilterRequest;
10+
import org.opentripplanner.transit.model.basic.MainAndSubMode;
11+
import org.opentripplanner.utils.collection.CollectionUtils;
12+
import org.opentripplanner.utils.collection.ListUtils;
13+
14+
class FilterMapper {
15+
16+
static List<TransitFilter> mapFilters(
17+
List<MainAndSubMode> modes,
18+
List<GraphQLTypes.GraphQLTransitFilterInput> filters
19+
) {
20+
var filterRequests = new ArrayList<TransitFilter>();
21+
for (var filterInput : filters) {
22+
var includes = filterInput.getGraphQLInclude();
23+
var excludes = filterInput.getGraphQLExclude();
24+
CollectionUtils.requireNullOrNonEmpty(includes, "filters.include");
25+
CollectionUtils.requireNullOrNonEmpty(excludes, "filters.exclude");
26+
27+
if (CollectionUtils.isEmpty(excludes) && CollectionUtils.isEmpty(includes)) {
28+
var typeName = GraphQLUtils.typeName(filterInput);
29+
throw new IllegalArgumentException(
30+
"%s must contain at least one 'include' or 'exclude'.".formatted(typeName)
31+
);
32+
}
33+
34+
var filterRequestBuilder = TransitFilterRequest.of();
35+
36+
// in the GTFS API modes can only be included but not excluded.
37+
if (CollectionUtils.isEmpty(includes)) {
38+
var modeSelect = SelectRequest.of().withTransportModes(modes).build();
39+
filterRequestBuilder.addSelect(modeSelect);
40+
} else {
41+
// for every inclusion we also need to add the modes, otherwise the filter will not work
42+
for (var selectInput : ListUtils.nullSafeImmutableList(includes)) {
43+
var builder = SelectRequestMapper.mapSelectRequest(selectInput, "include");
44+
builder.withTransportModes(modes);
45+
filterRequestBuilder.addSelect(builder.build());
46+
}
47+
}
48+
49+
for (var selectInput : ListUtils.nullSafeImmutableList(excludes)) {
50+
filterRequestBuilder.addNot(
51+
SelectRequestMapper.mapSelectRequest(selectInput, "exclude").build()
52+
);
53+
}
54+
55+
filterRequests.add(filterRequestBuilder.build());
56+
}
57+
58+
return filterRequests;
59+
}
60+
}

application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import graphql.schema.DataFetchingEnvironment;
88
import java.util.List;
9+
import java.util.Optional;
910
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
1011
import org.opentripplanner.apis.gtfs.mapping.TransitModeMapper;
1112
import org.opentripplanner.routing.api.request.StreetMode;
@@ -15,6 +16,7 @@
1516
import org.opentripplanner.routing.api.request.request.filter.SelectRequest;
1617
import org.opentripplanner.routing.api.request.request.filter.TransitFilterRequest;
1718
import org.opentripplanner.transit.model.basic.MainAndSubMode;
19+
import org.opentripplanner.utils.collection.CollectionUtils;
1820

1921
public class ModePreferencesMapper {
2022

@@ -23,9 +25,10 @@ public class ModePreferencesMapper {
2325
*/
2426
static void setModes(
2527
JourneyRequestBuilder journey,
26-
GraphQLTypes.GraphQLPlanModesInput modesInput,
28+
GraphQLTypes.GraphQLQueryTypePlanConnectionArgs args,
2729
DataFetchingEnvironment environment
2830
) {
31+
var modesInput = args.getGraphQLModes();
2932
var direct = modesInput.getGraphQLDirect();
3033
if (Boolean.TRUE.equals(modesInput.getGraphQLTransitOnly())) {
3134
journey.withDirect(new StreetRequest(StreetMode.NOT_SET));
@@ -40,7 +43,11 @@ static void setModes(
4043
var transit = modesInput.getGraphQLTransit();
4144
if (Boolean.TRUE.equals(modesInput.getGraphQLDirectOnly())) {
4245
journey.withTransit(TransitRequestBuilder::disable);
43-
} else if (transit != null) {
46+
} else if (transit == null) {
47+
// even if there are no transit modes set, we need to set the filter to get the route/agency
48+
// filters for flex
49+
setTransitFilters(journey, MainAndSubMode.all(), args);
50+
} else {
4451
var access = transit.getGraphQLAccess();
4552
if (access != null) {
4653
if (access.isEmpty()) {
@@ -72,11 +79,14 @@ static void setModes(
7279
validateStreetModes(journey.build());
7380

7481
var transitModes = getTransitModes(environment);
75-
if (transitModes != null) {
82+
if (transitModes == null) {
83+
// even when there are no transit modes set we need to set the filters because of the route/agency
84+
// includes/excludes
85+
setTransitFilters(journey, MainAndSubMode.all(), args);
86+
} else {
7687
if (transitModes.isEmpty()) {
7788
throw new IllegalArgumentException("Transit modes must not be empty.");
7889
}
79-
var filterRequestBuilder = TransitFilterRequest.of();
8090
var mainAndSubModes = transitModes
8191
.stream()
8292
.map(mode ->
@@ -87,13 +97,36 @@ static void setModes(
8797
)
8898
)
8999
.toList();
90-
filterRequestBuilder.addSelect(
91-
SelectRequest.of().withTransportModes(mainAndSubModes).build()
92-
);
93-
journey.withTransit(transitRequestBuilder ->
94-
transitRequestBuilder.setFilters(List.of(filterRequestBuilder.build()))
95-
);
100+
setTransitFilters(journey, mainAndSubModes, args);
96101
}
97102
}
98103
}
104+
105+
/**
106+
* It may be a little surprising that the transit filters are mapped here. This
107+
* is because the mapping function needs to know the modes to build the correct
108+
* select request as it needs to be the first one in each transit filter request.
109+
*/
110+
private static void setTransitFilters(
111+
JourneyRequestBuilder journey,
112+
List<MainAndSubMode> modes,
113+
GraphQLTypes.GraphQLQueryTypePlanConnectionArgs args
114+
) {
115+
var graphQlFilters = Optional.ofNullable(args.getGraphQLPreferences())
116+
.map(GraphQLTypes.GraphQLPlanPreferencesInput::getGraphQLTransit)
117+
.map(GraphQLTypes.GraphQLTransitPreferencesInput::getGraphQLFilters)
118+
.orElse(List.of());
119+
if (CollectionUtils.hasValue(graphQlFilters)) {
120+
var filters = FilterMapper.mapFilters(modes, graphQlFilters);
121+
journey.withTransit(b -> b.setFilters(filters));
122+
}
123+
// if there isn't a transit filter or a mode set, then we can keep the default which is to include
124+
// everything
125+
else if (!modes.equals(MainAndSubMode.all())) {
126+
var filter = TransitFilterRequest.of()
127+
.addSelect(SelectRequest.of().withTransportModes(modes).build())
128+
.build();
129+
journey.withTransit(b -> b.setFilters(List.of(filter)));
130+
}
131+
}
99132
}

application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public static RouteRequest toRouteRequest(
7171
);
7272

7373
request.withJourney(journeyRequestBuilder ->
74-
setModes(journeyRequestBuilder, args.getGraphQLModes(), environment)
74+
setModes(journeyRequestBuilder, args, environment)
7575
);
7676

7777
// sadly we need to use the raw collection because it is cast to the wrong type
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.opentripplanner.apis.gtfs.mapping.routerequest;
2+
3+
import static org.opentripplanner.utils.collection.CollectionUtils.requireNullOrNonEmpty;
4+
5+
import org.opentripplanner.apis.gtfs.GraphQLUtils;
6+
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
7+
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitFilterSelectInput;
8+
import org.opentripplanner.apis.gtfs.mapping.TransitModeMapper;
9+
import org.opentripplanner.routing.api.request.request.filter.SelectRequest;
10+
import org.opentripplanner.transit.model.basic.MainAndSubMode;
11+
import org.opentripplanner.transit.model.framework.FeedScopedId;
12+
import org.opentripplanner.utils.collection.CollectionUtils;
13+
14+
class SelectRequestMapper {
15+
16+
static SelectRequest.Builder mapSelectRequest(
17+
GraphQLTransitFilterSelectInput input,
18+
String name
19+
) {
20+
var routes = input.getGraphQLRoutes();
21+
var agencies = input.getGraphQLAgencies();
22+
requireNullOrNonEmpty(routes, "preferences.transit.filters.%s.routes".formatted(name));
23+
requireNullOrNonEmpty(agencies, "preferences.transit.filters.%s.agencies".formatted(name));
24+
25+
if (CollectionUtils.isEmpty(routes) && CollectionUtils.isEmpty(agencies)) {
26+
var type = GraphQLUtils.typeName(input);
27+
throw new IllegalArgumentException(
28+
"%s must contain at least one element in either 'routes or 'agencies'.".formatted(type)
29+
);
30+
}
31+
32+
var selectRequestBuilder = SelectRequest.of();
33+
34+
if (CollectionUtils.hasValue(routes)) {
35+
selectRequestBuilder.withRoutes(FeedScopedId.parse(routes));
36+
}
37+
38+
if (CollectionUtils.hasValue(agencies)) {
39+
selectRequestBuilder.withAgencies(FeedScopedId.parse(agencies));
40+
}
41+
42+
return selectRequestBuilder;
43+
}
44+
}

0 commit comments

Comments
 (0)