Skip to content

Commit bfc5ef0

Browse files
authored
feat: stop_times.txt and locations.geojson foreign key validation (#1951)
1 parent e9258a2 commit bfc5ef0

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.mobilitydata.gtfsvalidator.validator;
2+
3+
import javax.inject.Inject;
4+
import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator;
5+
import org.mobilitydata.gtfsvalidator.notice.ForeignKeyViolationNotice;
6+
import org.mobilitydata.gtfsvalidator.notice.NoticeContainer;
7+
import org.mobilitydata.gtfsvalidator.table.GtfsGeoJsonFeature;
8+
import org.mobilitydata.gtfsvalidator.table.GtfsGeoJsonFeaturesContainer;
9+
import org.mobilitydata.gtfsvalidator.table.GtfsStopTime;
10+
import org.mobilitydata.gtfsvalidator.table.GtfsStopTimeTableContainer;
11+
12+
@GtfsValidator
13+
public class LocationIdForeignKeyValidator extends FileValidator {
14+
private final GtfsGeoJsonFeaturesContainer locationGeoJSONTable;
15+
private final GtfsStopTimeTableContainer stopTimeTable;
16+
17+
@Inject
18+
public LocationIdForeignKeyValidator(
19+
GtfsGeoJsonFeaturesContainer locationGeoJSONTable, GtfsStopTimeTableContainer stopTimeTable) {
20+
this.locationGeoJSONTable = locationGeoJSONTable;
21+
this.stopTimeTable = stopTimeTable;
22+
}
23+
24+
@Override
25+
public boolean shouldCallValidate() {
26+
// The location_id column is optional, so we should only call validate if the column exists.
27+
return stopTimeTable != null
28+
&& locationGeoJSONTable != null
29+
&& stopTimeTable.hasColumn(GtfsStopTime.LOCATION_ID_FIELD_NAME);
30+
}
31+
32+
@Override
33+
public void validate(NoticeContainer noticeContainer) {
34+
for (GtfsStopTime stopTime : stopTimeTable.getEntities()) {
35+
String foreignKey = stopTime.locationId();
36+
if (foreignKey.isEmpty()) {
37+
continue;
38+
}
39+
if (!locationGeoJSONTable.byLocationIdMap().containsKey(foreignKey)) {
40+
noticeContainer.addValidationNotice(
41+
new ForeignKeyViolationNotice(
42+
GtfsStopTime.FILENAME,
43+
GtfsStopTime.LOCATION_ID_FIELD_NAME,
44+
GtfsGeoJsonFeature.FILENAME,
45+
GtfsGeoJsonFeature.FEATURE_ID_FIELD_NAME,
46+
foreignKey,
47+
stopTime.csvRowNumber()));
48+
}
49+
}
50+
}
51+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.mobilitydata.gtfsvalidator.validator;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
5+
import com.google.common.collect.ImmutableList;
6+
import java.util.List;
7+
import java.util.function.Consumer;
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.junit.runners.JUnit4;
11+
import org.mobilitydata.gtfsvalidator.notice.ForeignKeyViolationNotice;
12+
import org.mobilitydata.gtfsvalidator.notice.NoticeContainer;
13+
import org.mobilitydata.gtfsvalidator.table.*;
14+
15+
@RunWith(JUnit4.class)
16+
public class LocationIdForeignKeyValidatorTest {
17+
private static <T> T configure(T obj, Consumer<T> configurator) {
18+
configurator.accept(obj);
19+
return obj;
20+
}
21+
22+
@Test
23+
public void missingGeoJSONId_yieldsNotice() {
24+
NoticeContainer noticeContainer = new NoticeContainer();
25+
GtfsGeoJsonFileDescriptor descriptor = new GtfsGeoJsonFileDescriptor();
26+
List<GtfsGeoJsonFeature> geoJsonFeatures =
27+
ImmutableList.of(
28+
configure(
29+
new GtfsGeoJsonFeature(),
30+
f -> {
31+
f.setFeatureId("locationId");
32+
}));
33+
GtfsGeoJsonFeaturesContainer geoJsonFeaturesContainer =
34+
descriptor.createContainerForEntities(geoJsonFeatures, noticeContainer);
35+
List<GtfsStopTime> stopTimes =
36+
ImmutableList.of(
37+
new GtfsStopTime.Builder().setCsvRowNumber(1).setLocationId("locationId2").build());
38+
GtfsStopTimeTableContainer stopTimeTableContainer =
39+
GtfsStopTimeTableContainer.forEntities(stopTimes, noticeContainer);
40+
new LocationIdForeignKeyValidator(geoJsonFeaturesContainer, stopTimeTableContainer)
41+
.validate(noticeContainer);
42+
assertThat(noticeContainer.getValidationNotices())
43+
.containsExactly(
44+
new ForeignKeyViolationNotice(
45+
"stop_times.txt", "location_id", "locations.geojson", "id", "locationId2", 1));
46+
}
47+
48+
@Test
49+
public void existingGeoJSONId_yieldsNoNotice() {
50+
NoticeContainer noticeContainer = new NoticeContainer();
51+
GtfsGeoJsonFileDescriptor descriptor = new GtfsGeoJsonFileDescriptor();
52+
List<GtfsGeoJsonFeature> geoJsonFeatures =
53+
ImmutableList.of(
54+
configure(
55+
new GtfsGeoJsonFeature(),
56+
f -> {
57+
f.setFeatureId("locationId");
58+
}));
59+
GtfsGeoJsonFeaturesContainer geoJsonFeaturesContainer =
60+
descriptor.createContainerForEntities(geoJsonFeatures, noticeContainer);
61+
List<GtfsStopTime> stopTimes =
62+
ImmutableList.of(
63+
new GtfsStopTime.Builder().setCsvRowNumber(1).setLocationId("locationId").build());
64+
GtfsStopTimeTableContainer stopTimeTableContainer =
65+
GtfsStopTimeTableContainer.forEntities(stopTimes, noticeContainer);
66+
new LocationIdForeignKeyValidator(geoJsonFeaturesContainer, stopTimeTableContainer)
67+
.validate(noticeContainer);
68+
assertThat(noticeContainer.getValidationNotices()).isEmpty();
69+
}
70+
}

0 commit comments

Comments
 (0)