Skip to content

Commit d231a66

Browse files
authored
fix: loader should assume v1.0 if version field is missing (#182)
1 parent db8bfd8 commit d231a66

File tree

3 files changed

+87
-75
lines changed

3 files changed

+87
-75
lines changed

gbfs-validator-java-loader/src/main/java/org/entur/gbfs/validator/loader/Loader.java

Lines changed: 82 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,17 @@ public List<LoadedFile> load(String discoveryURIString, Authentication auth)
167167
JSONObject discoveryFileJson = new JSONObject(
168168
new JSONTokener(new ByteArrayInputStream(discoveryFileBytes))
169169
);
170-
String version = discoveryFileJson.getString("version");
170+
// Default to version 1.0 if no version field is present (as in GBFS v1.0)
171+
String version = discoveryFileJson.optString("version", "1.0");
171172

172173
List<LoadedFile> loadedFiles = new ArrayList<>();
174+
// Normalize discovery file name to "gbfs" (without extension) for validator compatibility
175+
String discoveryFileName = discoveryLoadedFile
176+
.fileName()
177+
.replaceFirst("\\.json$", "");
173178
loadedFiles.add(
174179
new LoadedFile(
175-
discoveryLoadedFile.fileName(),
180+
discoveryFileName,
176181
discoveryLoadedFile.url(),
177182
new ByteArrayInputStream(discoveryFileBytes),
178183
discoveryLoadedFile.language(),
@@ -200,35 +205,40 @@ private List<LoadedFile> getV3Files(
200205
) {
201206
List<LoadedFile> loadedFeedFiles = new ArrayList<>();
202207

203-
List<CompletableFuture<LoadedFile>> futures = discoveryFileJson
204-
.getJSONObject("data")
205-
.getJSONArray("feeds")
206-
.toList()
207-
.stream()
208-
.map(feed -> {
209-
@SuppressWarnings("unchecked")
210-
Map<String, Object> feedMap = (Map<String, Object>) feed;
211-
String url = (String) feedMap.get("url");
212-
String name = (String) feedMap.get("name");
213-
214-
return CompletableFuture.supplyAsync(
215-
() -> {
216-
LoadedFile loadedFile = loadFile(URI.create(url), auth);
217-
return new LoadedFile(
218-
name,
219-
url,
220-
loadedFile.fileContents(),
221-
loadedFile.language(),
222-
loadedFile.loaderErrors()
223-
);
224-
},
225-
executorService
226-
);
227-
})
228-
.toList();
229-
loadedFeedFiles.addAll(
230-
futures.stream().map(CompletableFuture::join).toList()
231-
);
208+
try {
209+
List<CompletableFuture<LoadedFile>> futures = discoveryFileJson
210+
.getJSONObject("data")
211+
.getJSONArray("feeds")
212+
.toList()
213+
.stream()
214+
.map(feed -> {
215+
@SuppressWarnings("unchecked")
216+
Map<String, Object> feedMap = (Map<String, Object>) feed;
217+
String url = (String) feedMap.get("url");
218+
String name = (String) feedMap.get("name");
219+
220+
return CompletableFuture.supplyAsync(
221+
() -> {
222+
LoadedFile loadedFile = loadFile(URI.create(url), auth);
223+
return new LoadedFile(
224+
name,
225+
url,
226+
loadedFile.fileContents(),
227+
loadedFile.language(),
228+
loadedFile.loaderErrors()
229+
);
230+
},
231+
executorService
232+
);
233+
})
234+
.toList();
235+
loadedFeedFiles.addAll(
236+
futures.stream().map(CompletableFuture::join).toList()
237+
);
238+
} catch (Exception e) {
239+
// If we can't parse the discovery file structure, return empty list
240+
// so the discovery file itself can be validated and report proper errors
241+
}
232242

233243
return loadedFeedFiles;
234244
}
@@ -241,42 +251,47 @@ private List<LoadedFile> getPreV3Files(
241251
List<LoadedFile> loadedFeedFiles = new ArrayList<>();
242252
List<CompletableFuture<LoadedFile>> futures = new ArrayList<>();
243253

244-
discoveryFileJson
245-
.getJSONObject("data")
246-
.keys()
247-
.forEachRemaining(languageKey -> {
248-
discoveryFileJson
249-
.getJSONObject("data")
250-
.getJSONObject(languageKey)
251-
.getJSONArray("feeds")
252-
.toList()
253-
.forEach(feed -> {
254-
@SuppressWarnings("unchecked")
255-
Map<String, Object> feedMap = (Map<String, Object>) feed;
256-
String url = (String) feedMap.get("url");
257-
String name = (String) feedMap.get("name");
258-
259-
futures.add(
260-
CompletableFuture.supplyAsync(
261-
() -> {
262-
LoadedFile loadedFile = loadFile(URI.create(url), auth);
263-
return new LoadedFile(
264-
name,
265-
url,
266-
loadedFile.fileContents(),
267-
languageKey,
268-
loadedFile.loaderErrors()
269-
);
270-
},
271-
executorService
272-
)
273-
);
274-
});
275-
});
276-
277-
loadedFeedFiles.addAll(
278-
futures.stream().map(CompletableFuture::join).toList()
279-
);
254+
try {
255+
discoveryFileJson
256+
.getJSONObject("data")
257+
.keys()
258+
.forEachRemaining(languageKey -> {
259+
discoveryFileJson
260+
.getJSONObject("data")
261+
.getJSONObject(languageKey)
262+
.getJSONArray("feeds")
263+
.toList()
264+
.forEach(feed -> {
265+
@SuppressWarnings("unchecked")
266+
Map<String, Object> feedMap = (Map<String, Object>) feed;
267+
String url = (String) feedMap.get("url");
268+
String name = (String) feedMap.get("name");
269+
270+
futures.add(
271+
CompletableFuture.supplyAsync(
272+
() -> {
273+
LoadedFile loadedFile = loadFile(URI.create(url), auth);
274+
return new LoadedFile(
275+
name,
276+
url,
277+
loadedFile.fileContents(),
278+
languageKey,
279+
loadedFile.loaderErrors()
280+
);
281+
},
282+
executorService
283+
)
284+
);
285+
});
286+
});
287+
288+
loadedFeedFiles.addAll(
289+
futures.stream().map(CompletableFuture::join).toList()
290+
);
291+
} catch (Exception e) {
292+
// If we can't parse the discovery file structure, return empty list
293+
// so the discovery file itself can be validated and report proper errors
294+
}
280295

281296
return loadedFeedFiles;
282297
}

gbfs-validator-java-loader/src/test/java/org/entur/gbfs/validator/loader/LoaderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ void testLoad_WithDiscoveryFileAndFeed_V3_WithAuth() throws IOException {
349349

350350
LoadedFile discoveryFile = files
351351
.stream()
352-
.filter(f -> f.fileName().equals("gbfs-v3.json"))
352+
.filter(f -> f.fileName().equals("gbfs-v3"))
353353
.findFirst()
354354
.orElse(null);
355355
LoadedFile systemInfoFile = files

gbfs-validator-java/src/main/java/org/entur/gbfs/validation/validator/GbfsJsonValidator.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,10 @@ private Version detectVersionFromParsedFeeds(
158158
) {
159159
ParsedFeedContainer gbfsContainer = parsedFeeds.get("gbfs");
160160
if (gbfsContainer != null && gbfsContainer.jsonObject() != null) {
161-
try {
162-
String versionStr = gbfsContainer.jsonObject().getString("version");
163-
if (versionStr != null) {
164-
return VersionFactory.createVersion(versionStr);
165-
}
166-
} catch (JSONException e) {
167-
LOG.warn("Could not extract version from gbfs.json, using default.", e);
161+
// Use optString to handle v1.0 feeds that don't have a version field
162+
String versionStr = gbfsContainer.jsonObject().optString("version", null);
163+
if (versionStr != null && !versionStr.isEmpty()) {
164+
return VersionFactory.createVersion(versionStr);
168165
}
169166
}
170167
return VersionFactory.createVersion(GbfsJsonValidator.DEFAULT_VERSION);

0 commit comments

Comments
 (0)