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

Release #213

Merged
merged 50 commits into from
Apr 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
45cca07
docs(github): add codeowners, issue and pr templates
evansiroky Feb 12, 2019
253cdc7
refactor(loader): refactor loader classes to make certain methods ava…
landonreed Feb 14, 2019
09c0e5b
refactor(Table): add hasCompoundKey fluent method
landonreed Feb 15, 2019
6774ccd
fix(Table): refactor table; add comments
landonreed Feb 15, 2019
92d8991
refactor: move methods to Table/Field classes
landonreed Feb 18, 2019
5d58f2a
refactor: add javadoc and refactor getCsvReader into Table
landonreed Feb 18, 2019
5ec5285
Merge pull request #200 from conveyal/mtc-enhancements-20190214
Feb 19, 2019
2f0cb34
fix(loader): refactor field loader to clean value and return errors
landonreed Feb 19, 2019
337e061
refactor(loader): refactor error storage for illegal chars
landonreed Feb 19, 2019
f4e89d9
test(field validator): add test for catching/removing illegal chars
landonreed Feb 19, 2019
67562cc
Merge branch 'dev' into refactor-field-validation
Feb 19, 2019
3be3d21
fix(load): handle bad refs found in calendar dates
landonreed Feb 20, 2019
1a91296
fix(snapshot): create feed registry if not exists when snapshotting
landonreed Feb 27, 2019
b2b554a
Merge branch 'dev' into fix-snapshot-on-blank-db
landonreed Feb 27, 2019
98194d0
feat(GraphQL): allow nested queries where fields don't match
landonreed Feb 27, 2019
978d4a3
refactor: reorganize methods so that helpers are alphabetized after t…
evansiroky Mar 1, 2019
1bf6d9a
test: Add a failing test case to prove existence of service_id change…
evansiroky Mar 1, 2019
51ef962
test(graphql): add new test for fetching stops with children
landonreed Mar 1, 2019
07479b4
style(white-space): fix javadoc formatting
landonreed Mar 1, 2019
52ea677
Merge pull request #204 from conveyal/fix-snapshot-on-blank-db
Mar 1, 2019
825314b
refactor(github): reword parts of the issue and pr templates
evansiroky Mar 5, 2019
546c2ad
Merge pull request #195 from conveyal/github-settings
Mar 5, 2019
af8baa1
test: update # stops expected in spatial index
landonreed Mar 6, 2019
456e3a0
Merge pull request #205 from conveyal/child-stops-query
Mar 6, 2019
263ee5a
fix(validate): re-enable overlapping trips check
landonreed Mar 13, 2019
fb45651
fix(validate): fix rounded travel times check
landonreed Mar 13, 2019
a2b0551
refactor: address PR comments
landonreed Mar 18, 2019
faeaf1f
feat(editor): normalize stop times travel times for set of pattern stops
landonreed Mar 18, 2019
15b7e52
refactor(editor): add TableWriter#close
landonreed Mar 18, 2019
4b7c74f
fix(normalize stop times): add test for and normalize stop times
landonreed Mar 25, 2019
1ca6405
refactor(overlapping-trips): add comments, clean up files
landonreed Mar 25, 2019
90d5c3e
fix(editor): only delete stop_times/freq when deleting a pattern
landonreed Mar 25, 2019
c23fb4c
chore: add javadoc for JdbcTableWriter#close
landonreed Mar 26, 2019
c67169a
test: fix broken test
landonreed Mar 26, 2019
3153d48
test: fix canNormalizePatternStopTimes test mistakes
landonreed Mar 26, 2019
df8d4eb
refactor(test): reformat informational file in resource dir to be in …
evansiroky Mar 27, 2019
01a7a19
chore(test): fix copy-pasta javadoc
Mar 28, 2019
e3fbc17
refactor(overlapping-trips): move block overlap check to ServiceValid…
landonreed Mar 28, 2019
f3c246e
refactor(editor): fix up SQL injection vulnerabilities
landonreed Mar 29, 2019
687dfe7
chore: fix typo
landonreed Mar 29, 2019
0145219
Merge pull request #201 from conveyal/refactor-field-validation
Mar 29, 2019
b8b8eaf
test(editor): improve tests for delete functionality
landonreed Mar 29, 2019
f23d194
test(fix): fix failing test (duplicate pattern id)
landonreed Mar 29, 2019
98a2ea8
Merge pull request #212 from conveyal/service-id-change-bug
Mar 29, 2019
7f41695
refactor(validation-result): revert changes made for overlapping-trips
landonreed Mar 29, 2019
2cd68f0
Merge pull request #208 from conveyal/overlapping-trips
Mar 29, 2019
bde91b8
Merge branch 'dev' into fill-missing-stop-times
landonreed Mar 29, 2019
2e4dd3a
refactor(editor): remove service id filter param from normalize stop …
landonreed Mar 29, 2019
18d5bbe
test(JdbcTableWriter): check that update to pattern_stop shape_dist_t…
landonreed Apr 1, 2019
b8ed46b
Merge pull request #210 from conveyal/fill-missing-stop-times
Apr 1, 2019
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
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# See https://help.github.com/articles/about-codeowners/

# A Conveyal employee is required to approve PR merges
* @conveyal/employees
17 changes: 17 additions & 0 deletions .github/issue_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
_**NOTE:** This issue system is intended for reporting bugs and tracking progress in software development. Although this software is licensed with an open-source license, any issue opened here may not be responded to in a timely manner. [Conveyal](https://www.conveyal.com) is unable to provide technical support for custom deployments of this software unless your company has a support contract with us. Please remove this note when creating the issue._

## Observed behavior

Please explain what is being observed within the application here.

## Expected behavior

Please explain what should happen instead.

## Steps to reproduce the problem

Please be as specific as possible.

## Notes on configuration and files required to reproduce the issue

Please describe any applicable config files that were used. If this issue is reporting behavior that is related to the data format of a particular GTFS file, please upload that GTFS file and note the exact files and lines that are causing the issue. If this issue is reproducable in [datatools-server](https://github.com/conveyal/datatools-server) or [datatools-ui](https://github.com/conveyal/datatools-ui) please include the exact commits of those libraries and any configuration of those libraries that differs from the default configuration.
13 changes: 13 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### Checklist

- [ ] Appropriate branch selected _(all PRs must first be merged to `dev` before they can be merged to `master`)_
- [ ] Any modified or new methods or classes have helpful JavaDoc and code is thoroughly commented
- [ ] The description lists all applicable issues this PR seeks to resolve
- [ ] The description lists any configuration setting(s) that differ from the default settings
- [ ] All tests and CI builds passing
- [ ] The description lists all relevant PRs included in this release _(remove this if not merging to master)_
- [ ] Code coverage does not significantly worsen (ideally it improves) _(remove this if not merging to master)_

### Description

Please explain the changes you made here and, if not immediately obvious from the code, how they resolve any referenced issues. Be sure to include all issues being resolved and any special configuration settings that are need for the software to run properly with these changes. If merging to master, please also list the PRs that are to be included.
2 changes: 1 addition & 1 deletion src/main/java/com/conveyal/gtfs/error/NewGTFSError.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public class NewGTFSError {

/** The class of the table in which the error was encountered. */
public final Class<? extends Entity> entityType;
public Class<? extends Entity> entityType;

/** The kind of error encountered. */
public final NewGTFSErrorType errorType;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/conveyal/gtfs/error/NewGTFSErrorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum NewGTFSErrorType {
TIME_FORMAT(Priority.MEDIUM, "Time format should be HH:MM:SS."),
URL_FORMAT(Priority.MEDIUM, "URL format should be <scheme>://<authority><path>?<query>#<fragment>"),
LANGUAGE_FORMAT(Priority.LOW, "Language should be specified with a valid BCP47 tag."),
ILLEGAL_FIELD_VALUE(Priority.MEDIUM, "Fields may not contain tabs, carriage returns or new lines."),
INTEGER_FORMAT(Priority.MEDIUM, "Incorrect integer format."),
FLOATING_FORMAT(Priority.MEDIUM, "Incorrect floating point number format."),
COLUMN_NAME_UNSAFE(Priority.HIGH, "Column header contains characters not safe in SQL, it was renamed."),
Expand Down Expand Up @@ -59,7 +60,7 @@ public enum NewGTFSErrorType {
TRAVEL_TIME_ZERO(Priority.HIGH, "The vehicle arrives at this stop at the same time it departs from the previous stop."),
MISSING_ARRIVAL_OR_DEPARTURE(Priority.MEDIUM, "First and last stop times are required to have both an arrival and departure time."),
TRIP_TOO_FEW_STOP_TIMES(Priority.MEDIUM, "A trip must have at least two stop times to represent travel."),
TRIP_OVERLAP_IN_BLOCK(Priority.MEDIUM, "Blocks"),
TRIP_OVERLAP_IN_BLOCK(Priority.MEDIUM, "A trip overlaps another trip and shares the same block_id."),
TRAVEL_TOO_SLOW(Priority.MEDIUM, "The vehicle is traveling very slowly to reach this stop from the previous one."),
TRAVEL_TOO_FAST(Priority.MEDIUM, "The vehicle travels extremely fast to reach this stop from the previous one."),
VALIDATOR_FAILED(Priority.HIGH, "The specified validation stage failed due to an error encountered during loading. This is likely due to an error encountered during loading (e.g., a date or number field is formatted incorrectly.)."),
Expand Down

This file was deleted.

12 changes: 12 additions & 0 deletions src/main/java/com/conveyal/gtfs/graphql/GraphQLGtfsSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,18 @@ public class GraphQLGtfsSchema {
.field(MapFetcher.field("parent_station"))
.field(MapFetcher.field("location_type", GraphQLInt))
.field(MapFetcher.field("wheelchair_boarding", GraphQLInt))
// Returns all stops that reference parent stop's stop_id
.field(newFieldDefinition()
.name("child_stops")
.type(new GraphQLList(new GraphQLTypeReference("stop")))
.dataFetcher(new JDBCFetcher(
"stops",
"stop_id",
null,
false,
"parent_station"
))
.build())
.field(RowCountFetcher.field("stop_time_count", "stop_times", "stop_id"))
.field(newFieldDefinition()
.name("patterns")
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/conveyal/gtfs/graphql/fetchers/JDBCFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class JDBCFetcher implements DataFetcher<List<Map<String, Object>>> {
final String parentJoinField;
private final String sortField;
private final boolean autoLimit;
private final String childJoinField;

/**
* Constructor for tables that need neither restriction by a where clause nor sorting based on the enclosing entity.
Expand Down Expand Up @@ -96,10 +97,22 @@ public JDBCFetcher (String tableName, String parentJoinField) {
* tables that it is unnatural to expect a limit (e.g., shape points or pattern stops).
*/
public JDBCFetcher (String tableName, String parentJoinField, String sortField, boolean autoLimit) {
this(tableName, parentJoinField, sortField, autoLimit, null);
}

/**
*
* @param childJoinField The child table field that should be joined to the parent join field. This enables joining
* where references from the child table to the parent table do not share the same field name,
* e.g., stops#stop_id -> transfers#from_stop_id. This value defaults to parentJoinField if
* argument is null.
*/
public JDBCFetcher (String tableName, String parentJoinField, String sortField, boolean autoLimit, String childJoinField) {
this.tableName = tableName;
this.parentJoinField = parentJoinField;
this.sortField = sortField;
this.autoLimit = autoLimit;
this.childJoinField = childJoinField != null ? childJoinField : parentJoinField;
}

// We can't automatically generate JDBCFetcher based field definitions for inclusion in a GraphQL schema (as we
Expand Down Expand Up @@ -198,8 +211,8 @@ List<Map<String, Object>> getResults (
// If we are fetching an item nested within a GTFS entity in the GraphQL query, we want to add an SQL "where"
// clause. This could conceivably be done automatically, but it's clearer to just express the intent.
// Note, this is assuming the type of the field in the parent is a string.
if (parentJoinField != null && parentJoinValues != null && !parentJoinValues.isEmpty()) {
whereConditions.add(makeInClause(parentJoinField, parentJoinValues, preparedStatementParameters));
if (childJoinField != null && parentJoinValues != null && !parentJoinValues.isEmpty()) {
whereConditions.add(makeInClause(childJoinField, parentJoinValues, preparedStatementParameters));
}
if (sortField != null) {
// Sort field is not provided by user input, so it's ok to add here (i.e., it's not prone to SQL injection).
Expand Down Expand Up @@ -341,7 +354,7 @@ List<Map<String, Object>> getResults (
}
// This logging produces a lot of noise during testing due to large numbers of joined sub-queries
// LOG.info("table name={}", tableName);
LOG.debug("SQL: {}", preparedStatement.toString());
LOG.info("SQL: {}", preparedStatement.toString());
if (preparedStatement.execute()) {
ResultSet resultSet = preparedStatement.getResultSet();
ResultSetMetaData meta = resultSet.getMetaData();
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/com/conveyal/gtfs/loader/BatchTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@ public void addBatch() throws SQLException {
}
}

public void executeRemaining() throws SQLException {
/**
* Execute any remaining statements and return the total records processed.
*/
public int executeRemaining() throws SQLException {
if (currentBatchSize > 0) {
totalRecordsProcessed += currentBatchSize;
preparedStatement.executeBatch();
currentBatchSize = 0;
}
// Avoid reuse, signal that this was cleanly closed.
preparedStatement = null;
LOG.info(String.format("Inserted %d %s records", totalRecordsProcessed, recordType));
LOG.info(String.format("Processed %d %s records", totalRecordsProcessed, recordType));
return totalRecordsProcessed;
}

public void finalize () {
Expand Down
20 changes: 13 additions & 7 deletions src/main/java/com/conveyal/gtfs/loader/BooleanField.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.conveyal.gtfs.loader;

import com.conveyal.gtfs.error.NewGTFSError;
import com.conveyal.gtfs.error.NewGTFSErrorType;
import com.conveyal.gtfs.storage.StorageException;

import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.SQLType;
import java.util.Set;

/**
* A GTFS boolean field, coded as a single character string 0 or 1.
Expand All @@ -17,17 +19,21 @@ public BooleanField (String name, Requirement requirement) {
super(name, requirement);
}

private boolean validate (String string) {
private ValidateFieldResult<Boolean> validate (String string) {
ValidateFieldResult<Boolean> result = new ValidateFieldResult<>();
if ( ! ("0".equals(string) || "1".equals(string))) {
throw new StorageException(NewGTFSErrorType.BOOLEAN_FORMAT, string);
result.errors.add(NewGTFSError.forFeed(NewGTFSErrorType.BOOLEAN_FORMAT, string));
}
return "1".equals(string);
result.clean = "1".equals(string);
return result;
}

@Override
public void setParameter (PreparedStatement preparedStatement, int oneBasedIndex, String string) {
public Set<NewGTFSError> setParameter (PreparedStatement preparedStatement, int oneBasedIndex, String string) {
try {
preparedStatement.setBoolean(oneBasedIndex, validate(string));
ValidateFieldResult<Boolean> result = validate(string);
preparedStatement.setBoolean(oneBasedIndex, result.clean);
return result.errors;
} catch (Exception ex) {
throw new StorageException(ex);
}
Expand All @@ -37,8 +43,8 @@ public void setParameter (PreparedStatement preparedStatement, int oneBasedIndex
* The 0 or 1 will be converted to the string "true" or "false" for SQL COPY.
*/
@Override
public String validateAndConvert (String string) {
return Boolean.toString(validate(string));
public ValidateFieldResult<String> validateAndConvert (String string) {
return ValidateFieldResult.from(validate(string));
}

@Override
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/com/conveyal/gtfs/loader/ColorField.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.conveyal.gtfs.loader;

import com.conveyal.gtfs.error.NewGTFSError;
import com.conveyal.gtfs.error.NewGTFSErrorType;
import com.conveyal.gtfs.storage.StorageException;

import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.SQLType;
import java.util.Set;

import static com.conveyal.gtfs.loader.Field.cleanString;

Expand All @@ -21,21 +23,24 @@ public ColorField(String name, Requirement requirement) {
}

/** Check that a string can be properly parsed and is in range. */
public String validateAndConvert (String string) {
public ValidateFieldResult<String> validateAndConvert (String string) {
ValidateFieldResult<String> result = new ValidateFieldResult<>(string);
try {
if (string.length() != 6) {
throw new StorageException(NewGTFSErrorType.COLOR_FORMAT, string);
result.errors.add(NewGTFSError.forFeed(NewGTFSErrorType.COLOR_FORMAT, string));
}
int integer = Integer.parseInt(string, 16);
return string; // Could also store the integer.
return result; // Could also store the integer.
} catch (Exception ex) {
throw new StorageException(NewGTFSErrorType.COLOR_FORMAT, string);
}
}

public void setParameter(PreparedStatement preparedStatement, int oneBasedIndex, String string) {
public Set<NewGTFSError> setParameter(PreparedStatement preparedStatement, int oneBasedIndex, String string) {
try {
preparedStatement.setString(oneBasedIndex, validateAndConvert(string));
ValidateFieldResult<String> result = validateAndConvert(string);
preparedStatement.setString(oneBasedIndex, result.clean);
return result.errors;
} catch (Exception ex) {
throw new StorageException(ex);
}
Expand Down
16 changes: 10 additions & 6 deletions src/main/java/com/conveyal/gtfs/loader/CurrencyField.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.conveyal.gtfs.loader;

import com.conveyal.gtfs.error.NewGTFSError;
import com.conveyal.gtfs.error.NewGTFSErrorType;
import com.conveyal.gtfs.storage.StorageException;
import com.google.common.collect.Sets;
Expand Down Expand Up @@ -198,21 +199,24 @@ public CurrencyField (String name, Requirement requirement) {
super(name, requirement);
}

private String validate (String string) {
private ValidateFieldResult<String> validate (String string) {
ValidateFieldResult<String> result = new ValidateFieldResult<>(string);
if (!CURRENCY_CODES.contains(string)) {
throw new StorageException(NewGTFSErrorType.CURRENCY_UNKNOWN, string);
result.errors.add(NewGTFSError.forFeed(NewGTFSErrorType.CURRENCY_UNKNOWN, string));
}
return string;
return result;
}

/** Check that a string can be properly parsed and is in range. */
public String validateAndConvert (String string) {
public ValidateFieldResult<String> validateAndConvert (String string) {
return validate(string);
}

public void setParameter(PreparedStatement preparedStatement, int oneBasedIndex, String string) {
public Set<NewGTFSError> setParameter(PreparedStatement preparedStatement, int oneBasedIndex, String string) {
try {
preparedStatement.setString(oneBasedIndex, validateAndConvert(string));
ValidateFieldResult<String> result = validateAndConvert(string);
preparedStatement.setString(oneBasedIndex, result.clean);
return result.errors;
} catch (Exception ex) {
throw new StorageException(ex);
}
Expand Down
27 changes: 19 additions & 8 deletions src/main/java/com/conveyal/gtfs/loader/DateField.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.conveyal.gtfs.loader;

import com.conveyal.gtfs.error.NewGTFSError;
import com.conveyal.gtfs.error.NewGTFSErrorType;
import com.conveyal.gtfs.storage.StorageException;

Expand All @@ -9,6 +10,8 @@
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Collections;
import java.util.Set;

/**
* A GTFS date in the format YYYYMMDD.
Expand All @@ -26,26 +29,33 @@ public DateField (String name, Requirement requirement) {
super(name, requirement);
}

public static String validate (String string) {
public static ValidateFieldResult<String> validate (String string) {
// Initialize default value as null (i.e., don't use the input value).
ValidateFieldResult<String> result = new ValidateFieldResult<>();
// Parse the date out of the supplied string.
LocalDate date;
try {
date = LocalDate.parse(string, GTFS_DATE_FORMATTER);
// Only set the clean result after the date parse is successful.
result.clean = string;
} catch (DateTimeParseException ex) {
throw new StorageException(NewGTFSErrorType.DATE_FORMAT, string);
result.errors.add(NewGTFSError.forFeed(NewGTFSErrorType.DATE_FORMAT, string));
return result;
}
// Range check on year. Parsing operation above should already have checked month and day ranges.
int year = date.getYear();
if (year < 2000 || year > 2100) {
throw new StorageException(NewGTFSErrorType.DATE_RANGE, string);
result.errors.add(NewGTFSError.forFeed(NewGTFSErrorType.DATE_RANGE, string));
}
return string;
return result;
}

@Override
public void setParameter (PreparedStatement preparedStatement, int oneBasedIndex, String string) {
public Set<NewGTFSError> setParameter (PreparedStatement preparedStatement, int oneBasedIndex, String string) {
try {
preparedStatement.setString(oneBasedIndex, validate(string));
ValidateFieldResult<String> result = validate(string);
preparedStatement.setString(oneBasedIndex, result.clean);
return result.errors;
} catch (Exception ex) {
throw new StorageException(ex);
}
Expand All @@ -54,17 +64,18 @@ public void setParameter (PreparedStatement preparedStatement, int oneBasedIndex
/**
* DateField specific method to set a statement parameter from a {@link LocalDate}.
*/
public void setParameter (PreparedStatement preparedStatement, int oneBasedIndex, LocalDate localDate) {
public Set<NewGTFSError> setParameter (PreparedStatement preparedStatement, int oneBasedIndex, LocalDate localDate) {
try {
if (localDate == null) setNull(preparedStatement, oneBasedIndex);
else preparedStatement.setString(oneBasedIndex, localDate.format(GTFS_DATE_FORMATTER));
return Collections.EMPTY_SET;
} catch (Exception e) {
throw new StorageException(e);
}
}

@Override
public String validateAndConvert (String string) {
public ValidateFieldResult<String> validateAndConvert (String string) {
return validate(string);
}

Expand Down
Loading