Skip to content

Support unrecognized properties for EV features#1500

Merged
dzinad merged 1 commit intomainfrom
NAVAND-713-dd
Oct 20, 2022
Merged

Support unrecognized properties for EV features#1500
dzinad merged 1 commit intomainfrom
NAVAND-713-dd

Conversation

@dzinad
Copy link
Copy Markdown
Contributor

@dzinad dzinad commented Oct 19, 2022

No description provided.

@dzinad dzinad requested a review from a team as a code owner October 19, 2022 09:13
@sonatype-lift
Copy link
Copy Markdown

sonatype-lift Bot commented Oct 19, 2022

⚠️ 5 God Classes were detected by Lift in this project. Visit the Lift web console for more details.

Comment thread CHANGELOG.md Outdated
* @param unrecognizedProperties parameters to add to request
*/
@NonNull
public Builder unrecognizedProperties(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not make the abstract unrecognized method public in DirectionsJsonObject.Builder? We definitely see a use-case for this method being public. Or add unrecognizedProperties as public function to the abstract builder if we don't want to expose the SerializableJsonElement.

cc @VysotskiVadim

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  1. It wasn't public so I thought there was a reason for it. So we should only make it public when needed, a white list, not black list;
  2. The unrecognized() method accepts Map<String, SerializableJsonElement> which is not very convenient for the end user, so yes, I'd prefer adding unrecognizedProperties with Map<String, JsonElement> instead.

So is there a reason why it's not public?

/**
* Provides utility methods to work with unrecognized properties.
*/
public final class UnrecognizedPropertiesUtils {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this be public? DirectionsJsonObject already exposes these accessors.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I want to access it from the service-directions-refresh-models module. To avoid code duplicating.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I imagine we don't need this if we expose the map getters.


abstract static class Builder<T extends Builder> {
@NonNull
abstract T unrecognized(@Nullable Map<String, SerializableJsonElement> value);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as above, why not making this public?


@Nullable
@UnrecognizedJsonProperties
abstract Map<String, SerializableJsonElement> unrecognized();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could consider making this public, refs NAVAND-778. Doesn't have to happen in this PR though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

How will making this public help NAVAND-778? For the case described there it's more like adding a method addUnrecognizedProperties instead of the current one that resets them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A public getter would allow us to first get the properties map, mutate them, and set them back. As we would do with any other builder object parameter. The getter and setter methods in this case need to work on the same map type so that developers don't need to do any parsing themselves, that's why I was hoping to expose a single function in the abstract class instead of adding explicit getter and setters manually everywhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK, makes sense but it's not very convenient to use with SerializableJsonElement. But I guess we could add convenience methods directly to DirectionsJsonObject and DirectionsRefreshJsonObject. WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sure, as we're discussing in #1500 (comment) JsonElement might be better.

*/
@NonNull
public Builder unrecognizedProperties(
@Nullable Map<String, JsonElement> unrecognizedProperties
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

RouteOptions.Builder exposes this map as Map<String, String>, we should align this one way or the other. For the general purpose JsonElement does look like a better candidate, but again only if we don't want to expose the SerializableJsonElement. @VysotskiVadim I remember you had some thoughts about it in the past.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, here we definitely need JsonElement. The structure will be complex.
For RouteOptions it's only about get parameters, that's why strings are used.
These are different use cases so I'm not sure if we should complicate RouteOptions usage with converting it all to JsonElement. But we could have 2 methods for RouteOptions to keep both consistency and usability.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@VysotskiVadim I remember you had some thoughts about it in the past.

the initial idea was to expose as little as possible to simplify future changes.

For RouteOptions it's only about get parameters, that's why strings are used.

that's right.

These are different use cases so I'm not sure if we should complicate RouteOptions usage with converting it all to JsonElement.

I agree with @dzinad. I don't see value in aligning unrecognised properties API between request and response models.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't fully agree. Whether we look at request or response object, they are all DirectionsJsonObjects (or refresh) and support the unrecognized fields in exactly the same way, and that's why I think they should also expose the same API to mutate or reset the properties.

We started with RouteOptions needing some form of unrecognized properties setter, now we need to add the same to LegAnnotation and this could be true at any point in time for any DirectionsJsonObject, depending on what's experimental in the Directions API at a certain time.

Couldn't we expose Map<String, JsonElement> getter and setters for all DirectionsJsonObjects?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can if we leave the convenience method for RouteOptions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why couldn't we use only Map<String, JsonElement> for RouteOptions as well?

The only change developers would have to make is wrap their string values in JsonPrimitive(value). That's actually even more flexible because other than string parameters are also acceptable by the Directions API. Of course we can leave the existing function in place but I'd rather deprecate it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  1. It's a breaking change and that's something that would definitely affect the users;
  2. In the majority of the cases users only need Map<String, String>. Making them use Map<String, JsonElement> will force them to write more boiler-plate code on their side.

I'm strongly in favour of leaving the convenience method as-is. We'll provide a more flexible options: if they want something other than strings, they can use the new method. And I'm not suggesting to add Map<String, String> everywhere: only leave it where it already is and where it makes sense to have it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's a breaking change and that's something that would definitely affect the users;

We'd not be removing the old function, only deprecating it.

In the majority of the cases users only need Map<String, String>. Making them use Map<String, JsonElement> will force them to write more boiler-plate code on their side.

Map<String, String> is limiting and actually somewhat incorrect. For example, look at ev_max_charge parameter in https://docs.mapbox.com/api/navigation/directions/#parameters-for-electric-vehicle-routing - it's an integer but we'd recommend (and today require) developers pass it as a string. JsonPrimitive(integer) would be more accurate here.

In any case, we can discuss and consider the deprecation separately, the old function has to stay in place regardless.

* Provides a base class for Directions model classes.
*/
public class DirectionsRefreshJsonObject implements Serializable {
public abstract class DirectionsRefreshJsonObject implements Serializable {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it's a breaking change, isn't it? Users who create the object won't be able to this, right? TBH I don't remember which visibility default constructor from java has 🙂

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

even if it's a breaking change we can consider the previous implementation as a bug. I believe we already did it once 🙂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TBH I don't remember which visibility default constructor from java has

It's public with no arguments.

it's a breaking change, isn't it?

You are right. Good catch! I see the following options here:

  1. Commit the breaking change and reduce the constructor visibility for the future. Why would anyone create an empty DirectionsRefreshJsonObject?.. But that would still break semver;
  2. Up the major version. I'm not sure about the policy on that. @LukasPaczos could you chime in here?
  3. Add unrecognized properties directly to DirectionsRouteRefresh. That's the only place they are needed right now. But it's not very flexible;
  4. Add compile-time support for EV features. Not really consistent with the feature being in beta;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actually I had the same problem adding unrecognized properties: https://github.com/mapbox/mapbox-java/pull/1394/files#r848311760 😄

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Haha, nice
Then maybe we should think about reducing the visibility of everything that should not be used directly? These are mostly constructors but it makes sense to double-check. That's a separate issue though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's make the change as last time around 👍

Then maybe we should think about reducing the visibility of everything that should not be used directly?

Good ticket to have for the next major version 👍

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

NAVAND-802

@dzinad dzinad force-pushed the NAVAND-713-dd branch 3 times, most recently from a3e8487 to 7be690f Compare October 20, 2022 16:00
Copy link
Copy Markdown
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.

LGTM

@dzinad dzinad enabled auto-merge (rebase) October 20, 2022 18:22
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.

3 participants