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
6 changes: 3 additions & 3 deletions gbfs-validator-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
</scm>
<properties>
<jdk.version>17</jdk.version>
<gbfsGithubUrl>https://github.com/MobilityData/gbfs-json-schema/archive/refs/tags/v4.0.0.zip</gbfsGithubUrl>
<schemaVersion>4.0.0</schemaVersion>
<gbfsGithubUrl>https://github.com/MobilityData/gbfs-json-schema/archive/refs/tags/v4.3.0.zip</gbfsGithubUrl>
<schemaVersion>4.3.0</schemaVersion>

<everit-json-schema.version>1.14.6</everit-json-schema.version>
<slf4j.version>2.0.17</slf4j.version>
Expand Down Expand Up @@ -310,7 +310,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
<additionalOptions>-Xdoclint:none</additionalOptions>
</configuration>
<executions>
<execution>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ private record ParsedFeedContainer(
"station_status",
"free_bike_status",
"vehicle_status",
"vehicle_availability",
"manifest",
"system_hours",
"system_alerts",
"system_alerts",
"system_calendar",
"system_regions",
"system_pricing_plans",
"system_alerts",
"geofencing_zones"
);

Expand Down Expand Up @@ -110,17 +111,10 @@ public ValidationResult validate(Map<String, InputStream> rawFeeds) {
}

if (parsedContainer.jsonObject() == null) {
// Parsing failed or stream read error
FileValidationResult result = new FileValidationResult(
FileValidationResult result = createParsingErrorResult(
feedName,
version.isFileRequired(feedName),
true,
0,
version.getSchema(feedName).toString(),
parsedContainer.originalContent(),
null,
Collections.emptyList(),
parsedContainer.parsingErrors()
parsedContainer,
version
);
fileValidations.put(feedName, result);
} else {
Expand Down Expand Up @@ -183,19 +177,10 @@ public FileValidationResult validateFile(String fileName, InputStream file) {
ParsedFeedContainer parsedContainer = parseFeed(fileName, file);

if (parsedContainer.jsonObject() == null) {
// Determine version for schema and requirement - this is tricky for a single file
// For now, using default version. A more robust approach might require context.
Version tempVersion = VersionFactory.createVersion(DEFAULT_VERSION);
return new FileValidationResult(
return createParsingErrorResult(
fileName,
tempVersion.isFileRequired(fileName),
true, // File was provided
0,
tempVersion.getSchema(fileName).toString(),
parsedContainer.originalContent(),
null, // File specific version unknown
Collections.emptyList(),
parsedContainer.parsingErrors()
parsedContainer,
VersionFactory.createVersion(DEFAULT_VERSION)
);
} else {
return validateFile(
Expand Down Expand Up @@ -350,4 +335,58 @@ private ParsedFeedContainer parseFeed(String name, InputStream raw) {
);
}
}

/**
* Creates a validation result for a file that could not be parsed. Parsing can fail before
* the exact version for a single file is known, so this resolves the best schema version
* available and still returns file metadata alongside the parse errors.
*
* @param feedName The GBFS feed name
* @param parsedContainer The parsed container holding original content and parse errors
* @param preferredVersion The initially preferred GBFS version
* @return A file validation result containing parse errors and any schema metadata that could be resolved
*/
private FileValidationResult createParsingErrorResult(
String feedName,
ParsedFeedContainer parsedContainer,
Version preferredVersion
) {
Version schemaVersion = resolveVersionForFeed(feedName, preferredVersion);
boolean supportedFeed = schemaVersion.getFileNames().contains(feedName);

return new FileValidationResult(
feedName,
supportedFeed && schemaVersion.isFileRequired(feedName),
true,
0,
supportedFeed ? schemaVersion.getSchema(feedName).toString() : null,
parsedContainer.originalContent(),
null,
Collections.emptyList(),
parsedContainer.parsingErrors()
);
}

/**
* Resolves the version to use for a feed when building parse-error results. Some feed types
* may not exist in the initially detected version, so this falls back to the newest version
* that supports the feed in order to point the error result at a schema when possible.
*
* @param feedName The GBFS feed name
* @param preferredVersion The initially detected or preferred GBFS version
* @return The preferred version when it supports the feed, otherwise the newest version that does
*/
private Version resolveVersionForFeed(
String feedName,
Version preferredVersion
) {
if (preferredVersion.getFileNames().contains(feedName)) {
return preferredVersion;
}

return VersionFactory.createLatestVersionSupportingFeed(
feedName,
preferredVersion.getVersionString()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class Version30 extends AbstractVersion {

public static final String VERSION = "3.0";

private static final List<String> feeds = Arrays.asList(
private static final List<String> FEEDS = Arrays.asList(
"gbfs",
"gbfs_versions",
"system_information",
Expand All @@ -51,37 +51,36 @@ public class Version30 extends AbstractVersion {
"geofencing_zones"
);

private static final Map<String, List<CustomRuleSchemaPatcher>> customRules =
Map.of(
"vehicle_types",
List.of(new NoInvalidReferenceToPricingPlansInVehicleTypes()),
"station_status",
List.of(
new NoInvalidReferenceToVehicleTypesInStationStatus(),
new NoMissingVehicleTypesAvailableWhenVehicleTypesExists(),
new NoInvalidReferenceToStation("station_information")
static final Map<String, List<CustomRuleSchemaPatcher>> CUSTOM_RULES = Map.of(
"vehicle_types",
List.of(new NoInvalidReferenceToPricingPlansInVehicleTypes()),
"station_status",
List.of(
new NoInvalidReferenceToVehicleTypesInStationStatus(),
new NoMissingVehicleTypesAvailableWhenVehicleTypesExists(),
new NoInvalidReferenceToStation("station_information")
),
"vehicle_status",
List.of(
new NoMissingOrInvalidVehicleTypeIdInVehicleStatusWhenVehicleTypesExist(
"vehicle_status"
),
"vehicle_status",
List.of(
new NoMissingOrInvalidVehicleTypeIdInVehicleStatusWhenVehicleTypesExist(
"vehicle_status"
),
new NoMissingCurrentRangeMetersInVehicleStatusForMotorizedVehicles(
"vehicle_status"
),
new NoInvalidReferenceToPricingPlansInVehicleStatus("vehicle_status")
new NoMissingCurrentRangeMetersInVehicleStatusForMotorizedVehicles(
"vehicle_status"
),
"system_information",
List.of(new NoMissingStoreUriInSystemInformation("vehicle_status")),
"station_information",
List.of(
new NoInvalidReferenceToRegionInStationInformation(),
new NoInvalidReferenceToStation("station_status")
)
);
new NoInvalidReferenceToPricingPlansInVehicleStatus("vehicle_status")
),
"system_information",
List.of(new NoMissingStoreUriInSystemInformation("vehicle_status")),
"station_information",
List.of(
new NoInvalidReferenceToRegionInStationInformation(),
new NoInvalidReferenceToStation("station_status")
)
);

protected Version30() {
super(VERSION, feeds, customRules);
super(VERSION, FEEDS, CUSTOM_RULES);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
*
* * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by
* * the European Commission - subsequent versions of the EUPL (the "Licence");
* * You may not use this work except in compliance with the Licence.
* * You may obtain a copy of the Licence at:
* *
* * https://joinup.ec.europa.eu/software/page/eupl
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the Licence is distributed on an "AS IS" basis,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the Licence for the specific language governing permissions and
* * limitations under the Licence.
*
*/

package org.mobilitydata.gbfs.validation.validator.versions;

import java.util.Arrays;
import java.util.List;

public class Version31RC3 extends AbstractVersion {

public static final String VERSION = "3.1-RC3";

private static final List<String> FEEDS = Arrays.asList(
"gbfs",
"gbfs_versions",
"system_information",
"vehicle_types",
"station_information",
"station_status",
"vehicle_status",
"manifest",
"system_regions",
"system_pricing_plans",
"system_alerts",
"geofencing_zones",
"vehicle_availability"
);

protected Version31RC3() {
super(VERSION, FEEDS, Version30.CUSTOM_RULES);
}

@Override
public boolean isFileRequired(String file) {
return super.isFileRequired(file) || "gbfs".equals(file);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,21 @@

package org.mobilitydata.gbfs.validation.validator.versions;

import java.util.List;

public class VersionFactory {

private static final List<String> SUPPORTED_VERSIONS_DESC = List.of(
"3.1-RC3",
"3.0",
"2.3",
"2.2",
"2.1",
"2.0",
"1.1",
"1.0"
);

private VersionFactory() {}

public static Version createVersion(String version) {
Expand All @@ -38,8 +51,22 @@ public static Version createVersion(String version) {
return new Version23();
case "3.0":
return new Version30();
case "3.1-RC3":
return new Version31RC3();
default:
throw new UnsupportedOperationException("Version not implemented");
}
}

public static Version createLatestVersionSupportingFeed(
String feedName,
String fallbackVersion
) {
return SUPPORTED_VERSIONS_DESC
.stream()
.map(VersionFactory::createVersion)
.filter(version -> version.getFileNames().contains(feedName))
.findFirst()
.orElse(createVersion(fallbackVersion));
}
}
Loading
Loading