Skip to content

Conversation

@korshaknn
Copy link
Contributor

@korshaknn korshaknn commented Feb 20, 2020

removed bearing validation logic from MapboxDirections
now both RouteOptions and MapboxDirections use FormatUtils.formatBearings() to build a valid string from a list of bearings

closes #1120

@korshaknn korshaknn added the Bug label Feb 20, 2020
@korshaknn korshaknn added this to the v5.0.0 milestone Feb 20, 2020
@korshaknn korshaknn self-assigned this Feb 20, 2020
protected void writePoint(JsonWriter out, Point value) throws IOException {
writePointList(out, value.coordinates());
protected void writePoint(JsonWriter out, Point point) throws IOException {
if (point == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We are now choosing to ignore nulls, while we should actually be able to support them when serializing and deserializing Lists in the BaseCoordinatesTypeAdapter. I think we'll need to refactor both read and write methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukasPaczos what logic should writePoint has in case of null Point?
also readPoint always returns non null Point or throws an exception.

Copy link
Contributor

Choose a reason for hiding this comment

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

also readPoint always returns non null Point or throws an exception

That's what we'd need to refactor. Could we use JsonToken.NULL or a similar construct?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this because before null wasn't an option when reach serialization / deserialization and now it is? I believe the problem is that before we were always dealing with Strings and not anymore 🤔 How serialization / deserialization was working before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Guardiola31337 @LukasPaczos I think it's only because of waypointTargetsList() in RouteOptions.
Previously waypointTargets were stored as a String, so PointAsCoordinatesTypeAdapter was not used. But now they are a List<Point>, so PointAsCoordinatesTypeAdapter calls BaseCoordinatesTypeAdapter.writePoint().
It is possible to store nulls in waypointTargetsList, so we need to check for a null inside writePoint() method

Copy link
Contributor

Choose a reason for hiding this comment

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

@korshaknn could we add some tests in RouteOptions verifying that the serialization / deserialization works 👌 (toJson / fromJson)?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's right @korshaknn, but we still need to serialize and deserialize this null, not skip it. I think the inconsistency will be caught with a test that @Guardiola31337 suggested.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukasPaczos how that null Point should be serialized?

Copy link
Contributor

Choose a reason for hiding this comment

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

how that null Point should be serialized?

Probably "" (empty)? Could we run a quick test of toJson / fromJson before and after #1118 to check how was working, compare and do the same?

Copy link
Contributor

Choose a reason for hiding this comment

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

Empty string sounds correct.

Copy link
Contributor

@Guardiola31337 Guardiola31337 left a comment

Choose a reason for hiding this comment

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

Apart from the comments below, when debugging this PR I also noticed that in DirectionsResponseFactory#generateRouteOptions we're using the deprecated annotations API instead of annotationsList 👀

protected void writePoint(JsonWriter out, Point value) throws IOException {
writePointList(out, value.coordinates());
protected void writePoint(JsonWriter out, Point point) throws IOException {
if (point == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this because before null wasn't an option when reach serialization / deserialization and now it is? I believe the problem is that before we were always dealing with Strings and not anymore 🤔 How serialization / deserialization was working before?

.bearings(bearings)
.accessToken(ACCESS_TOKEN)
.build();
assertEquals(";;45,90;2,90;",
Copy link
Contributor

Choose a reason for hiding this comment

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

Debugging this PR I found that ParseUtils#parseToListOfListOfDoubles in this concrete example is not working as expected

@Test
public void checksParseToListOfListOfDoublesEmptyTrailing() {
  List<List<Double>> bearings = ParseUtils.parseToListOfListOfDoubles(";;45,90;2,90;");

  assertEquals(5, bearings.size());
}

It should return a list of 5 elements and it's returning a list of 4 because of not splitting with trailing empty strings

After changing to original.split(SEMICOLON, -1); other tests start failing. We should revisit the empty trailing scenarios for other fields too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tested it with @kmadsen , @Guardiola31337 you are right, bearing string should HAVE trailing ; if we pass null bearings.
I'll fix logic and tests

Copy link
Contributor

Choose a reason for hiding this comment

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

@korshaknn is ";;45,90;2,90;" and ";;45,90;2,90" going to be equal in this case? I think it shouldn't be. We should take the strategy that Directions API uses:

  • what happens if we pass "45,90;" as the request parameter? Is it considered as 2 values (1 empty)?
  • what happens if we pass "45,90;;" as the request parameter? Is it considered as 3 values (2 empty)?
    If yes, we shouldn't trim the trailing empty string.

/cc @danpaz

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, looks like we commented at the same time @korshaknn :) Thanks for verifying!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukasPaczos
";;45,90;2,90;" will be valid for 5 coordinates
";;45,90;2,90" will be valid for 4 coordinates

@korshaknn
Copy link
Contributor Author

korshaknn commented Feb 24, 2020

@LukasPaczos @Guardiola31337
I tested RouteOptions.waypointTargets logic before refactoring and got the next results:

waypointTargets("11,22;33,44;55,66") -> "waypoint_targets":"11,22;33,44;55,66"

waypointTargets("11,22;33,44;") -> "waypoint_targets":"11,22;33,44;"

waypointTargets("11,22;;55,66") -> "waypoint_targets":"11,22;;55,66"

waypointTargets(";33,44;55,66") -> "waypoint_targets":";33,44;55,66"

I decided to return old logic and use String to store waypointTargets, in that case there is no need to change serialization/deserialization logic, it won't break anything.
so there will be 2 methods in builder:
abstract waypointTargets(String)
nonAbstract waypointTargetsList(List) (it uses FormatUtils.formatPointsList to store as s string)

to get waypointTargets from RouteOptions we can call
String waypointTargets
or List<Point> waypointTargetsList (it uses ParseUtils.parseToPoints to get a List from a string)

commit

@LukasPaczos
Copy link
Contributor

The only concern I have is that if the end-users (and our SDKs) are going to rely on the Lists implementation (because it's more convenient and that was the goal), with every call to the non-abstract List method we will have to parse the string and create new objects, again and again.

Did you run into any issues when trying to serialize/deserialize nulls?

@korshaknn
Copy link
Contributor Author

@LukasPaczos no, I haven't had any issues with serialization/deserialization.
BTW with old implementation we got a string and converted it to a new List. So now we just have the same logic inside RouteOptions

@LukasPaczos
Copy link
Contributor

BTW with old implementation we got a string and converted it to a new List. So now we just have the same logic inside RouteOptions

The question is whether we can hopefully skip one of those steps and optimize the process. If you had no issues with serialization, could we try it?

@korshaknn
Copy link
Contributor Author

korshaknn commented Feb 24, 2020

@LukasPaczos
if we store waypointTargets as List<Point>, RouteOptions will have 2 List<Point> :
List<Point> coordinates
and
List<Point> waypointTargets
so, they both will use the same serialization/deserialization adapter. But they have to be serialized in a different way.
"coordinates":[[1.0,2.0],[3.0,4.0]] - a list
"waypoint_targets":"33,44;55,66" - a string with semicolons

so I think it's better to store waypointTargets as a String
BTW, MapboxDirections also stores waypointTargets as a String. When you pass a List, it converts it with FormatUtils

Copy link
Contributor

@LukasPaczos LukasPaczos left a comment

Choose a reason for hiding this comment

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

Alright, let's just try it out in this case. We can continue iterating if needed.

@Guardiola31337
Copy link
Contributor

@korshaknn

But they have to be serialized in a different way.

Is this needed? Can't those be serialized / deserialized equally? Would that help solving @LukasPaczos's concern?

@korshaknn
Copy link
Contributor Author

@Guardiola31337 it was serialized in a different way before refactoring.
If we change serialization logic It may work incorrect in some cases. That's why I'm a little afraid of those changes.
Where do we send a json? Can it break any external (API) logic?
Where do we receive json from? Currently we receive a json which stores coordinates as a list and waypointTragets as a String. What if we change logic to handle waypointTragets as List, but someone sends us a String?

or maybe serialization/deserialization of RouteOptions is just an internal logic and doesn't affect other parts of the SDK and we can change it without any problems?

@Guardiola31337
Copy link
Contributor

@korshaknn

it was serialized in a different way before refactoring.
If we change serialization logic It may work incorrect in some cases. That's why I'm a little afraid of those changes.
Where do we send a json? Can it break any external (API) logic?
Where do we receive json from? Currently we receive a json which stores coordinates as a list and waypointTragets as a String. What if we change logic to handle waypointTragets as List, but someone sends us a String?

These are valid questions 👍

or maybe serialization/deserialization of RouteOptions is just an internal logic and doesn't affect other parts of the SDK and we can change it without any problems?

That's a great question 🤔 Events maybe? It'd be great to find out testing downstream in the Navigation SDK and 👀 if everything is working 👌 or if there are any 💥 We can do that in a separate PR, so feel free to merge here 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RouteOptions parsing issuse

3 participants