Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,17 @@ public List<LoadedFile> load(String discoveryURIString, Authentication auth)
JSONObject discoveryFileJson = new JSONObject(
new JSONTokener(new ByteArrayInputStream(discoveryFileBytes))
);
String version = discoveryFileJson.getString("version");
// Default to version 1.0 if no version field is present (as in GBFS v1.0)
String version = discoveryFileJson.optString("version", "1.0");

List<LoadedFile> loadedFiles = new ArrayList<>();
// Normalize discovery file name to "gbfs" (without extension) for validator compatibility
String discoveryFileName = discoveryLoadedFile
.fileName()
.replaceFirst("\\.json$", "");
loadedFiles.add(
new LoadedFile(
discoveryLoadedFile.fileName(),
discoveryFileName,
discoveryLoadedFile.url(),
new ByteArrayInputStream(discoveryFileBytes),
discoveryLoadedFile.language(),
Expand Down Expand Up @@ -200,35 +205,40 @@ private List<LoadedFile> getV3Files(
) {
List<LoadedFile> loadedFeedFiles = new ArrayList<>();

List<CompletableFuture<LoadedFile>> futures = discoveryFileJson
.getJSONObject("data")
.getJSONArray("feeds")
.toList()
.stream()
.map(feed -> {
@SuppressWarnings("unchecked")
Map<String, Object> feedMap = (Map<String, Object>) feed;
String url = (String) feedMap.get("url");
String name = (String) feedMap.get("name");

return CompletableFuture.supplyAsync(
() -> {
LoadedFile loadedFile = loadFile(URI.create(url), auth);
return new LoadedFile(
name,
url,
loadedFile.fileContents(),
loadedFile.language(),
loadedFile.loaderErrors()
);
},
executorService
);
})
.toList();
loadedFeedFiles.addAll(
futures.stream().map(CompletableFuture::join).toList()
);
try {
List<CompletableFuture<LoadedFile>> futures = discoveryFileJson
.getJSONObject("data")
.getJSONArray("feeds")
.toList()
.stream()
.map(feed -> {
@SuppressWarnings("unchecked")
Map<String, Object> feedMap = (Map<String, Object>) feed;
String url = (String) feedMap.get("url");
String name = (String) feedMap.get("name");

return CompletableFuture.supplyAsync(
() -> {
LoadedFile loadedFile = loadFile(URI.create(url), auth);
return new LoadedFile(
name,
url,
loadedFile.fileContents(),
loadedFile.language(),
loadedFile.loaderErrors()
);
},
executorService
);
})
.toList();
loadedFeedFiles.addAll(
futures.stream().map(CompletableFuture::join).toList()
);
} catch (Exception e) {
// If we can't parse the discovery file structure, return empty list
// so the discovery file itself can be validated and report proper errors
}

return loadedFeedFiles;
}
Expand All @@ -241,42 +251,47 @@ private List<LoadedFile> getPreV3Files(
List<LoadedFile> loadedFeedFiles = new ArrayList<>();
List<CompletableFuture<LoadedFile>> futures = new ArrayList<>();

discoveryFileJson
.getJSONObject("data")
.keys()
.forEachRemaining(languageKey -> {
discoveryFileJson
.getJSONObject("data")
.getJSONObject(languageKey)
.getJSONArray("feeds")
.toList()
.forEach(feed -> {
@SuppressWarnings("unchecked")
Map<String, Object> feedMap = (Map<String, Object>) feed;
String url = (String) feedMap.get("url");
String name = (String) feedMap.get("name");

futures.add(
CompletableFuture.supplyAsync(
() -> {
LoadedFile loadedFile = loadFile(URI.create(url), auth);
return new LoadedFile(
name,
url,
loadedFile.fileContents(),
languageKey,
loadedFile.loaderErrors()
);
},
executorService
)
);
});
});

loadedFeedFiles.addAll(
futures.stream().map(CompletableFuture::join).toList()
);
try {
discoveryFileJson
.getJSONObject("data")
.keys()
.forEachRemaining(languageKey -> {
discoveryFileJson
.getJSONObject("data")
.getJSONObject(languageKey)
.getJSONArray("feeds")
.toList()
.forEach(feed -> {
@SuppressWarnings("unchecked")
Map<String, Object> feedMap = (Map<String, Object>) feed;
String url = (String) feedMap.get("url");
String name = (String) feedMap.get("name");

futures.add(
CompletableFuture.supplyAsync(
() -> {
LoadedFile loadedFile = loadFile(URI.create(url), auth);
return new LoadedFile(
name,
url,
loadedFile.fileContents(),
languageKey,
loadedFile.loaderErrors()
);
},
executorService
)
);
});
});

loadedFeedFiles.addAll(
futures.stream().map(CompletableFuture::join).toList()
);
} catch (Exception e) {
// If we can't parse the discovery file structure, return empty list
// so the discovery file itself can be validated and report proper errors
}

return loadedFeedFiles;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ void testLoad_WithDiscoveryFileAndFeed_V3_WithAuth() throws IOException {

LoadedFile discoveryFile = files
.stream()
.filter(f -> f.fileName().equals("gbfs-v3.json"))
.filter(f -> f.fileName().equals("gbfs-v3"))
.findFirst()
.orElse(null);
LoadedFile systemInfoFile = files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,10 @@ private Version detectVersionFromParsedFeeds(
) {
ParsedFeedContainer gbfsContainer = parsedFeeds.get("gbfs");
if (gbfsContainer != null && gbfsContainer.jsonObject() != null) {
try {
String versionStr = gbfsContainer.jsonObject().getString("version");
if (versionStr != null) {
return VersionFactory.createVersion(versionStr);
}
} catch (JSONException e) {
LOG.warn("Could not extract version from gbfs.json, using default.", e);
// Use optString to handle v1.0 feeds that don't have a version field
String versionStr = gbfsContainer.jsonObject().optString("version", null);
if (versionStr != null && !versionStr.isEmpty()) {
return VersionFactory.createVersion(versionStr);
}
}
return VersionFactory.createVersion(GbfsJsonValidator.DEFAULT_VERSION);
Expand Down