Skip to content

Commit

Permalink
Merge pull request #21 from ibi-group/bike-gbfs-url-fix
Browse files Browse the repository at this point in the history
Use proper URLs for bike rental GBFS endpoints
  • Loading branch information
evansiroky authored Nov 13, 2019
2 parents 0942a69 + 820e16d commit 5b30790
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 6 deletions.
23 changes: 21 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,26 @@ before_install:
# install mkdocs
- pip install --user mkdocs

# Notify us of the build status on the Slack channel
notifications:
email: false
webhooks: https://outlook.office.com/webhook/03fa4a79-572f-4c68-b756-e4e851d0215a@9093f1a3-8771-4fb7-8596-d51eeef18cda/TravisCI/0aec73ecb8eb4ad696ea27dd3c6a102d/286c079f-6085-4aa0-8f8d-e2a3e8d1f568
# Notify us of the build status on MS Teams
webhooks: https://outlook.office.com/webhook/03fa4a79-572f-4c68-b756-e4e851d0215a@9093f1a3-8771-4fb7-8596-d51eeef18cda/TravisCI/0aec73ecb8eb4ad696ea27dd3c6a102d/286c079f-6085-4aa0-8f8d-e2a3e8d1f568
before_deploy:
# Get branch name of current branch for use in jar name: https://graysonkoonce.com/getting-the-current-branch-name-during-a-pull-request-in-travis-ci/
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
# Copy shaded jar over to deploy dir as git-described jar (either version or version+commit) and branch-specific jar.
- mkdir deploy
- cp target/*-shaded.jar deploy/otp-$(git describe --always).jar
- cp target/*-shaded.jar deploy/otp-latest-$BRANCH.jar
deploy:
provider: s3
access_key_id: AKIA2YG5XQ2YDBGQMBSN
secret_access_key:
secure: aIDBpMyoSAohVq5TP3U4sYDTO2G4LeKuf8SJxmGCjDlohNRNQTqXHhjh8hlp7u9kwMPNuYuk/SqX0JYI9jIaUNDmEYcjfYcTm8BP+TF4ITwO7yDUvAXfOrRo9BprFvTWd+NW8946bZzkoNJB2hfYoUrB0HFb0yLOzVyG5He/V8qkFBf99eeSYMn3/nbBEjoSm5jVXQNuw/LqT4XA0O3AwHRWrKRoOYAfYk2L5MmQjbq/UnCUEjBHSDw4QBv3BCSjmEHv7KSlg5b1puFz+AeZiLsLFVxvJlJbtmBfeDnr3xGYjTVvJIdDN7ExF8udQ0ZuJG9uD8ObArzYwOw051uJOz5JRW3ldXQp0MSvhKephjjv1ezAc3Nw9B8oOCwrRdGK0ns2i9gGANBf19L9aYPGPsir6pdZu2uGvc0ryLIoUmtP0TK9xO3bInYCgnmdSsDcvjmZAzBwBmGE+50sorYb2r569AqH7eQ5q0T46hn1tyXE1oNW6KZRd/pZflZA4WC1KK522dHshgHnksDpsTHnax1SSkbLInnhUqcBI9u0EJ0ECDzJwRrAlXxqO1qGllLxR8vfVz7ORq7kfDOgKUj4CnWIAW6nsc9GeGcN+x4Ihbroed+DIyneieSzp8Z8iy/94pQqrE1Q0xt9akyxFKYlGg2YHO+otVhU9+3WKNWRKnI=
bucket: otp-repo
local-dir: deploy
acl: public_read
skip_cleanup: true
on:
repo: ibi-group/OpenTripPlanner
all_branches: true
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
package org.opentripplanner.updater.bike_rental;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import org.opentripplanner.routing.bike_rental.BikeRentalStation;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.updater.JsonConfigurable;
import org.opentripplanner.updater.vehicle_rental.GBFSMappings.GbfsResponse;
import org.opentripplanner.util.NonLocalizedString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

import static org.opentripplanner.util.HttpUtils.getDataFromUrlOrFile;

/**
* Created by demory on 2017-03-14.
*/
public class GbfsBikeRentalDataSource implements BikeRentalDataSource, JsonConfigurable {

private static final Logger LOG = LoggerFactory.getLogger(GbfsBikeRentalDataSource.class);

private static final ObjectMapper mapper = new ObjectMapper();

private GbfsStationDataSource stationInformationSource; // station_information.json required by GBFS spec
private GbfsStationStatusDataSource stationStatusSource; // station_status.json required by GBFS spec
private GbfsFloatingBikeDataSource floatingBikeSource; // free_bike_status.json declared OPTIONAL by GBFS spec

private String baseUrl;

private String networkName;
// The language id to use in the GBFS.json. Default is "en".
private String language = "en";

/** Some car rental systems and flex transit systems work exactly like bike rental, but with cars. */
private boolean routeAsCar;
Expand All @@ -43,13 +52,11 @@ public GbfsBikeRentalDataSource (String networkName) {
public void setBaseUrl (String url) {
baseUrl = url;
if (!baseUrl.endsWith("/")) baseUrl += "/";
stationInformationSource.setUrl(baseUrl + "station_information.json");
stationStatusSource.setUrl(baseUrl + "station_status.json");
floatingBikeSource.setUrl(baseUrl + "free_bike_status.json");
}

@Override
public boolean update() {
updateUrls();
// These first two GBFS files are required.
boolean updatesFound = stationInformationSource.update();
updatesFound |= stationStatusSource.update();
Expand All @@ -59,6 +66,123 @@ public boolean update() {
return updatesFound;
}

/**
* Reads the GBFS.json url (if it exists) and sets the urls of the other sources
*/
private void updateUrls() {
// fetch data from root URL. This file/endpoint is actually not required per the GBFS spec
// See https://github.com/NABSA/gbfs/blob/master/gbfs.md#files
InputStream rootData = fetchFromUrl(makeGbfsEndpointUrl("gbfs.json"));

// Check to see if data from the root url was able to be fetched. The gbfs.json file is not required.
if (rootData == null) {
// root data is null, however some feeds don't specifically have a gbfs.json endpoint and just use the root
// url itself. Therefore try again with the root url to see if that works.
rootData = fetchFromUrl(makeGbfsEndpointUrl(""));
}

if (rootData == null) {
// Root GBFS.json file not able to be fetched, set default endpoints.
stationInformationSource.setUrl(makeGbfsEndpointUrl("station_information.json"));
stationStatusSource.setUrl(makeGbfsEndpointUrl("station_status.json"));
floatingBikeSource.setUrl(makeGbfsEndpointUrl("free_bike_status.json"));
} else {
// GBFS.json file is found. Parse data from response and set all of the corresponding URLs as they are
// available in the response data.
GbfsResponse gbfsResponse = null;
try {
gbfsResponse = mapper.readValue(rootData, GbfsResponse.class);
} catch (IOException e) {
LOG.error("failed to deserialize gbfs.json response: {}", e);
return;
} finally {
try {
rootData.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (gbfsResponse.data == null) {
LOG.error("failed to read gbfs.json, no data found");
return;
}

// Get the GBFS feeds for the configured language.
GbfsResponse.GbfsFeeds feeds = gbfsResponse.data.get(language);
if (feeds == null) {
LOG.error("requested language ({}) not available in GBFS: {}", language, baseUrl);
return;
}

// iterate through all feed endpoints and update as needed
for (GbfsResponse.GbfsFeed feed : feeds.feeds) {
switch (feed.name) {
case "system_information":
// FIXME: not supported yet
break;
case "station_information":
stationInformationSource.setUrl(feed.url);
break;
case "station_status":
stationStatusSource.setUrl(feed.url);
break;
case "free_bike_status":
floatingBikeSource.setUrl(feed.url);
break;
case "system_hours":
// FIXME: not supported yet
break;
case "system_calendar":
// FIXME: not supported yet
break;
case "system_regions":
// FIXME: not supported yet
break;
case "system_pricing_plans":
// FIXME: not supported yet
break;
case "system_alerts":
// FIXME: not supported yet
break;
}
}
}
}

/**
* Construct a url based on the root url and the desired file
*/
private String makeGbfsEndpointUrl(String file) {
return String.format("%s%s", baseUrl, file);
}

/**
* Helper method to fetch from a URL where a response is not required.
*/
private InputStream fetchFromUrl(String url) {
return fetchFromUrl(url, false);
}

/**
* Helper to fetch data from a URL, or file or something.
*
* @param url The URL or file or something to fetch from
* @param fetchRequired whether or not a failed fetch of any data should warrant logging an error
* @return An inputStream if successful or null if not successful
*/
private InputStream fetchFromUrl(String url, boolean fetchRequired) {
InputStream data = null;
try {
data = getDataFromUrlOrFile(url);
} catch (IOException e) {
LOG.warn("Failed to fetch from url: {}. Error: {}", url, e);
}
if (data == null && fetchRequired) {
LOG.error("Received no data from URL fetch from: {}", url);
}
return data;
}

@Override
public List<BikeRentalStation> getStations() {

Expand Down Expand Up @@ -99,6 +223,10 @@ public void configure (Graph graph, JsonNode jsonNode) {
throw new IllegalArgumentException("Missing mandatory 'url' configuration.");
}
this.setBaseUrl(url);
String language = jsonNode.path("language").asText();
if (language != null && language != "") {
this.language = language;
}
this.routeAsCar = jsonNode.path("routeAsCar").asBoolean(false);
if (routeAsCar) {
LOG.info("This 'bike rental' system will be treated as a car rental system.");
Expand Down

0 comments on commit 5b30790

Please sign in to comment.