Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add currentFuelPercent and currentRangeMeters to RentalVehichle in the GTFS GraphQL API #6272

Open
wants to merge 26 commits into
base: dev-2.x
Choose a base branch
from

Conversation

JustCris654
Copy link
Contributor

@JustCris654 JustCris654 commented Nov 22, 2024

Summary

Add currentFuelPercent and currentRangeMeters fields to RentalVehicle in the graphql GTFS API

Issue

issue

I added currentRangeMeters on top of the issue because it is related

Unit tests

Write a few words on how the new code is tested.

  • Graphql integration test have been modified to test this fields
  • I tested the query with the graphiql frontend
  • Do all tests
    pass the continuous integration service
    ? Yes

Documentation

  • I wrote the proper doc comments in the graphql schema

@JustCris654 JustCris654 changed the title Rental vehicle new gbfs fields Add currentFuelPercent and currentRangeMeters to RentalVehichle in the GTFS GraphQL API Nov 22, 2024
Copy link

codecov bot commented Nov 22, 2024

Codecov Report

Attention: Patch coverage is 63.26531% with 18 lines in your changes missing coverage. Please review.

Project coverage is 69.78%. Comparing base (72019c0) to head (ff8411a).
Report is 5 commits behind head on dev-2.x.

Files with missing lines Patch % Lines
...ental/datasources/GbfsFreeVehicleStatusMapper.java 52.17% 10 Missing and 1 partial ⚠️
...org/opentripplanner/transit/model/basic/Ratio.java 63.63% 4 Missing ⚠️
.../opentripplanner/transit/model/basic/Distance.java 66.66% 1 Missing and 1 partial ⚠️
.../apis/transmodel/model/stop/RentalVehicleType.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             dev-2.x    #6272      +/-   ##
=============================================
- Coverage      69.79%   69.78%   -0.01%     
- Complexity     17943    17958      +15     
=============================================
  Files           2046     2048       +2     
  Lines          76685    76729      +44     
  Branches        7829     7836       +7     
=============================================
+ Hits           53520    53546      +26     
- Misses         20423    20439      +16     
- Partials        2742     2744       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@JustCris654 JustCris654 marked this pull request as ready for review November 22, 2024 16:40
@JustCris654 JustCris654 requested a review from a team as a code owner November 22, 2024 16:40
@JustCris654 JustCris654 marked this pull request as draft November 26, 2024 06:40
@JustCris654 JustCris654 force-pushed the rentalVehicle_new_gbfs_fields branch from 2eef86c to 00a1e4b Compare November 27, 2024 11:54
@JustCris654 JustCris654 marked this pull request as ready for review December 2, 2024 11:13
@optionsome
Copy link
Member

Does the library we use validate that the fuel percent is between 0 and 1 or should we do it?

@JustCris654
Copy link
Contributor Author

Does the library we use validate that the fuel percent is between 0 and 1 or should we do it?

The library validate if the value is between 0 and 1 in output (when generating the graphql response).
It returns an error like this:

{
  "errors": [
    {
      "message": "Can't serialize value (/rentalVehicles[0]/currentFuelPercent) : Value is under 0 or greater than 1.",
      "path": [
        "rentalVehicles",
        0,
        "currentFuelPercent"
      ],
      "extensions": {
        "classification": "DataFetchingException"
      }
    }
  ],
  "data": {
    "rentalVehicles": [
      {
        "name": "Ninebot A200",
        "lat": 43.772581,
        "lon": 13.132542,
        "currentFuelPercent": null
      }
    ]
  }
}

The value is not checked when received in the gbfs mapper, do we need to check it also there? I saw that in the RatioScalarTest.java has this

    var ratio = (Double) GraphQLScalars.RATIO_SCALAR.getCoercing().parseValue(HALF);

to parse a value.

@optionsome
Copy link
Member

The value is not checked when received in the gbfs mapper, do we need to check it also there?

I would prefer to log some warning and ignore the value in the mapper instead of letting it be cause issues in the APIs.

@optionsome
Copy link
Member

@testower do you know if there is some general policy in the GBFS java library for validating values (such as not allowing over 100% fuel percent or negative range meters)?

@testower
Copy link
Contributor

testower commented Dec 9, 2024

@testower do you know if there is some general policy in the GBFS java library for validating values (such as not allowing over 100% fuel percent or negative range meters)?

The java model used in otp does not have validation annotations, so anything that can be deserialized is accepted.

@optionsome
Copy link
Member

The java model used in otp does not have validation annotations, so anything that can be deserialized is accepted.

Is there an option to use validation annotations? I'm just wondering should we discuss the possibility in tomorrow's dev meeting, for example?

@testower
Copy link
Contributor

testower commented Dec 9, 2024

Not with the current library. It is left out with intention. I don't think validation should be handled in the deserialization step anyway. This is runtime code / hot code path, so I think OTP should validate the data it needs to in the business layer.

@testower
Copy link
Contributor

testower commented Dec 9, 2024

I should probably add some reasoning: I don't think it's desirable to reject the deserialization of a whole file update, because some particular piece of data in that update doesn't validate according to the validation annotations. The data may still be perfectly usable by OTP.

@leonardehrenfried
Copy link
Member

You need to resolve merge conflicts.

@JustCris654 JustCris654 force-pushed the rentalVehicle_new_gbfs_fields branch 2 times, most recently from b52ebca to 284133a Compare December 17, 2024 13:48
// if the propulsion type has an engine current_range_meters is required
if (
vehicle.getVehicleTypeId() != null &&
vehicleTypes.get(vehicle.getVehicleTypeId()) != null &&
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this check, can you double check if it's right?
I'm not sure but vehicle_type_id is REQUIRED if the vehicle_types.json file is defined, that file is REQUIRED for systems with free_bike_status.json and if this file is not included then all vehicles are non motorized bicycles.
Therefore if the vehicleTypeId is not present in the vehicleTypes map I can assume that the file vehicle_types.json is not present and all vehicles are not motorized, so the propulsion type is human and range is not needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct, but this is in an area where a lot of feeds get things wrong so this validation might cause issues but I'm not sure if I'm against this or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spoke to @hbruch about this and he said that there are a number of feeds that don't include it where they should but he is in favour of enforcing the spec anyway. If we are not strict with data producers, they will never learn.


@Override
public String toString() {
return this.ratio.toString();
Copy link
Member

@optionsome optionsome Dec 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the toString output the number as a percent (like 24.5%), I'm not sure. With cost for example we have a custom toString format.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this today in the dev meeting and decided that the to string should just return the double as string, not a percent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so it's fine as it is

vehicle.getCurrentFuelPercent(),
e.getMessage()
);
} catch (NullPointerException e) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to catch npe, when would it happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current_fuel_percent is an optional field so getCurrentFuelPercent could return null

// if the propulsion type has an engine current_range_meters is required
if (
vehicle.getVehicleTypeId() != null &&
vehicleTypes.get(vehicle.getVehicleTypeId()) != null &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct, but this is in an area where a lot of feeds get things wrong so this validation might cause issues but I'm not sure if I'm against this or not.

RentalVehicleType.PropulsionType.HUMAN &&
rangeMeters == null
) {
return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should log a warning here since the whole vehicle is ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the check to be compliant to the GBFS standard and I think it's better this way in the future but I see that it could be an issue if feeds get this wrong. I can remove the check if you say it's for the best, I don't think it would break anything.
Maybe I can add just a warning and return the object?

@JustCris654 JustCris654 force-pushed the rentalVehicle_new_gbfs_fields branch from fc01a87 to 26d8d7d Compare January 7, 2025 14:52
import org.junit.jupiter.api.Test;
import org.opentripplanner.transit.model.basic.Ratio;

public class RatioTest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

Copy link
Member

@t2gran t2gran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked the Ratio implementation and how to provide a factory method witch handles validation errors. As mentioned in the dev-meeting today, my first attempt was to use a Result object - this was rather ugly and did not increase the readability or lowered the complexity(maintenance cost). So, after a bit of refactoring I went for a solution witch uses an error-handler and return an Optional - this was easy to implement and looks good on the caller side.

Please merge in these changes:

https://github.com/opentripplanner/OpenTripPlanner/tree/pr-6272

or cherry-pick commits:

The the two commits in the branch above fixes ALL my suggestions below, but I wanted to leave the comments so you can understand why I changed the code.


@Override
public int hashCode() {
return Objects.hash(this.ratio);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use the Double not Objects hash function here.


@Override
public boolean equals(Object other) {
if (other instanceof Ratio ratio) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use 'instanceof' in equals methods, follw the same practise as other value-objects.

*/
public class Ratio {

private final Double ratio;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private final Double ratio;
private final double ratio;

return this.ratio.toString();
}

public Double asDouble() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public Double asDouble() {
public double asDouble() {

@t2gran
Copy link
Member

t2gran commented Jan 9, 2025

@testower do you know if there is some general policy in the GBFS java library for validating values (such as not allowing over 100% fuel percent or negative range meters)?

The java model used in otp does not have validation annotations, so anything that can be deserialized is accepted.

Just to be clear - We do not want "insane" values in OTP. The DTOs used to import can accept it, but not the core OTP model. In general there is two categories of data:

  • pass-through data, witch we can be a bit loose on the validation and let the consumer take action (OTP API clients).
  • data used by the business logic in OTP - where we do validate more strict to avoid complex logic in OTP. This category also include fields we currently does not use, but we are likely to use in a planning request in the future. This apply to fuelRatio.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants