Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.conveyal.gtfs.graphql.fetchers;
import com.conveyal.gtfs.graphql.GTFSGraphQL;
import com.conveyal.gtfs.graphql.GraphQLGtfsSchema;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLList;
import org.apache.commons.dbutils.DbUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.conveyal.gtfs.graphql.GraphQLUtil.multiStringArg;
import static com.conveyal.gtfs.graphql.GraphQLUtil.stringArg;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
/**
* A generic fetcher to get fields out of an SQL database table.
*/
public class JDBCFetcher implements DataFetcher>> {
public static final Logger LOG = LoggerFactory.getLogger(JDBCFetcher.class);
// Make this an option to the GraphQL query.
private static final int DEFAULT_ROWS_TO_FETCH = 50;
private static final int MAX_ROWS_TO_FETCH = 500;
// Symbolic constants for argument names used to prevent misspellings.
public static final String ID_ARG = "id";
public static final String LIMIT_ARG = "limit";
public static final String OFFSET_ARG = "offset";
public static final String SEARCH_ARG = "search";
public static final String DATE_ARG = "date";
public static final String FROM_ARG = "from";
public static final String TO_ARG = "to";
public static final String MIN_LAT = "minLat";
public static final String MIN_LON = "minLon";
public static final String MAX_LAT = "maxLat";
public static final String MAX_LON = "maxLon";
// Lists of column names to be used when searching for string matches in the respective tables.
private static final String[] stopSearchColumns = new String[]{"stop_id", "stop_code", "stop_name"};
private static final String[] routeSearchColumns = new String[]{"route_id", "route_short_name", "route_long_name"};
// The following lists of arguments are considered non-standard, i.e., they are not handled by filtering entities
// with a simple WHERE clause. They are all bundled together in argsToSkip as a convenient way to pass over them
// when constructing said WHERE clause.
private static final List boundingBoxArgs = Arrays.asList(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
private static final List dateTimeArgs = Arrays.asList("date", "from", "to");
private static final List otherNonStandardArgs = Arrays.asList(SEARCH_ARG, LIMIT_ARG, OFFSET_ARG);
private static final List argsToSkip = Stream.of(boundingBoxArgs, dateTimeArgs, otherNonStandardArgs)
.flatMap(Collection::stream)
.collect(Collectors.toList());
public final String tableName;
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.
* These would typically be at the topmost level, directly inside a feed rather than nested in some GTFS entity type.
*/
public JDBCFetcher (String tableName) {
this(tableName, null);
}
/**
* @param tableName the database table from which to fetch rows.
* @param parentJoinField The field in the enclosing level of the Graphql query to use in a where clause.
* This allows e.g. selecting all the stop_times within a trip, using the enclosing trip's trip_id.
* If null, no such clause is added.
*/
public JDBCFetcher (String tableName, String parentJoinField) {
this(tableName, parentJoinField, null, true);
}
/**
*
* @param sortField The field on which to sort the list or fetched rows (in ascending order only).
* If null, no sort is included.
* @param autoLimit Whether to by default apply a limit to the fetched rows. This is used for certain
* 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
// do for MapFetcher for example). This is because we need custom inclusion of sub-tables in each table type.
// Still maybe we could make the most basic ones this way (automatically). Keeping this function as an example.
public static GraphQLFieldDefinition field (String tableName) {
return newFieldDefinition()
.name(tableName)
.type(new GraphQLList(GraphQLGtfsSchema.routeType))
.argument(stringArg("namespace"))
.argument(multiStringArg("route_id"))
.dataFetcher(new JDBCFetcher(tableName, null))
.build();
}
// Horrifically, we're going from SQL response to Gtfs-lib Java model object to GraphQL Java object to JSON.
// What if we did direct SQL->JSON?
// Could we transform JDBC ResultSets directly to JSON?
// With Jackson streaming API we can make a ResultSet serializer: https://stackoverflow.com/a/8120442
// We could apply a transformation from ResultSet to Gtfs-lib model object, but then more DataFetchers
// need to be defined to pull the fields out of those model objects. I'll try to skip those intermediate objects.
// Unfortunately we can't just apply DataFetchers directly to the ResultSets because they're cursors, and we'd
// have to somehow advance them at the right moment. So we need to transform the SQL results into fully materialized
// Java objects, then transform those into GraphQL fields. Fortunately the final transformation is trivial fetching
// from a Map.
// But what are the internal GraphQL objects, i.e. what does an ExecutionResult return? Are they Map?
@Override
public List