Refactor annotations parameter to accept explicit values#3628
Refactor annotations parameter to accept explicit values#3628
Conversation
…es annotations_type, add unit_tests
c877747 to
45a8d81
Compare
4079ace to
9a67fb4
Compare
features/testbot/matching.feature
Outdated
| | abci | abci | 1:1:10.008842:1,0:0:0:0,1:1:10.008842:0,0:0:0:0,1:1:10.010367:0 | | ||
| | trace | matchings | annotation | | ||
| | abeh | abeh | 1:10.008842:1:1:1,0:0:0:2:0,1:10.008842:1:2:0,1:10.008842:1:3:0,1:10.008842:1:4:0,0:0:0:4:0,2:19.906475:2:5:0,1:10.008842:1:6:0 | | ||
| | abci | abci | 1:10.008842:1:1:1,0:0:0:2:0,1:10.008842:1:2:0,0:0:0:2:0,1:10.010367:1:3:0 | |
features/testbot/matching.feature
Outdated
|
|
||
| When I match I should get | ||
| | trace | matchings | annotation | | ||
| | ach | ach | 1,1,0,1,1,2,1 | |
There was a problem hiding this comment.
The annotations here are durations, right? How do I read those in the context of the node map? Why is there a duration 0 in there?
features/testbot/matching.feature
Outdated
| | node | id | | ||
| | a | 1 | | ||
| | b | 2 | | ||
| | c | 3 | |
| annotation.values["weight"] = std::move(weights); | ||
| annotation.values["nodes"] = std::move(nodes); | ||
| annotation.values["datasources"] = std::move(datasources); | ||
| if (parameters.annotations_type & RouteParameters::AnnotationsType::Duration) |
There was a problem hiding this comment.
Hm either you do the bit fiddling here or what about abstracting it away? The code here could work no matter how the AnnotationsType is implemented.
| nodes.values.push_back(static_cast<std::uint64_t>(node_id)); | ||
| }); | ||
| annotation.values["nodes"] = std::move(nodes); | ||
| } |
There was a problem hiding this comment.
Hm the ~100 lines above are all doing the same:
- check if annotation x was requested
- create
json::Arrayfor many x - extract
step.xfromRouteStepand add into array - add array to annotations response as
"x" : [...]
I think you could abstract over this and make it extensible for the future at the same time.
What about abstracting this behind a function which takes
- a
AnnotationsType - a lambda taking a
RouteStepand returning annotations items out of it (e.g.step.durationforAnnotationsType::Duration - a string describing the annotations type for the json object's key as in
"key": [...]above
Example callsite:
makeAnnotation(AnnotationsType::Duration, "duration", [](auto& step){ return step.duration });which then does all of the steps above behind the scenes and wires the parts together.
There was a problem hiding this comment.
👍 refactoring this part is already in the OP as the todo item Abstract annotation insertion to steps into a function
I was going to take a template based approach to the function, but I can consider your suggestion as possible solution as well.
| Full, | ||
| False | ||
| }; | ||
| enum class AnnotationsType : int |
There was a problem hiding this comment.
Why the explicit : int here? Should be int by default.
| Args... args_) | ||
| : BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_}, | ||
| annotations_type{annotations_}, annotations{true}, geometries{geometries_}, | ||
| overview{overview_}, continue_straight{continue_straight_} |
There was a problem hiding this comment.
Nice 👍 This should keep the API stable for the time being. We may want to ticket the removal of all those hacks for v6. I think we already have tickets for these kinds of hacks.
| "nodes", engine::api::RouteParameters::AnnotationsType::Nodes)( | ||
| "distance", engine::api::RouteParameters::AnnotationsType::Distance)( | ||
| "weight", engine::api::RouteParameters::AnnotationsType::Weight)( | ||
| "datasources", engine::api::RouteParameters::AnnotationsType::Datasources); |
There was a problem hiding this comment.
Hm we need a mapping AnnotationsType -> string here, too. The same is needed for the response generation (as commented above). Maybe we should add this mapping already next to the AnnotationsType enum class definition. This would guarantee that we're in sync with name and enum item.
There was a problem hiding this comment.
using karma or a map value->string? would it make sense to make json output completely in karma?
There was a problem hiding this comment.
a value->string mapping would be good enough for now - our json handling is all based on a variant, not sure how karma fits in there
| overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1]); | ||
| overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1]) | | ||
| (qi::lit("annotations=") > | ||
| annotations_type[ph::bind(add_annotation, qi::_r1, qi::_1)] % ','); |
There was a problem hiding this comment.
Is an empty list allowed by this? (I don't know => make a unit test)
There was a problem hiding this comment.
Added! to the parameters_parser unit test
| RouteParameters::AnnotationsType::Weight | | ||
| RouteParameters::AnnotationsType::Nodes)), | ||
| true); | ||
| BOOST_CHECK_EQUAL(result_15->annotations, true); |
There was a problem hiding this comment.
Order should not matter - care to create a test expressing this?
Also edge cases such as
- empty list
- all possibilities
- all possibilities including All, None and combinations
| "nodes", engine::api::RouteParameters::AnnotationsType::Nodes)( | ||
| "distance", engine::api::RouteParameters::AnnotationsType::Distance)( | ||
| "weight", engine::api::RouteParameters::AnnotationsType::Weight)( | ||
| "datasources", engine::api::RouteParameters::AnnotationsType::Datasources); |
There was a problem hiding this comment.
using karma or a map value->string? would it make sense to make json output completely in karma?
| enum class AnnotationsType : int | ||
| { | ||
| None = 0, | ||
| Duration = 1 << 1, |
There was a problem hiding this comment.
here it can start from 1<<0, but better to use explicit numerals 0x01, 0x02, ...
54721eb to
47a02df
Compare
| return cb(new Error('Annotation not found in response', a_type)); | ||
| got[k] = annotation[a_type]; | ||
| } | ||
| }); |
There was a problem hiding this comment.
Hm this is dup. code with above
include/engine/api/route_api.hpp
Outdated
| annotations_store.values.reserve(leg.annotations.size()); | ||
| std::for_each(leg.annotations.begin(), | ||
| leg.annotations.end(), | ||
| [Get, &annotations_store](const guidance::LegGeometry::Annotation &step) { |
There was a problem hiding this comment.
I think you can simply say const auto& step here
include/engine/api/route_api.hpp
Outdated
| annotation.values["duration"] = | ||
| GetAnnotations(leg_geometry, | ||
| std::bind(&guidance::LegGeometry::Annotation::duration, | ||
| std::placeholders::_1)); |
There was a problem hiding this comment.
Instead of bind you can simply use a lambda, which is probably easier to read and does not have all the weird quirks bind has (see https://www.youtube.com/watch?v=zt7ThwVfap0). E.g.
[](const auto& v) { return v.distance };| CHECK_EQUAL_RANGE(reference_2.hints, result_2->hints); | ||
| BOOST_CHECK_EQUAL( | ||
| static_cast<bool>(result_2->annotations_type & RouteParameters::AnnotationsType::All), | ||
| true); |
There was a problem hiding this comment.
Hm maybe add operator== overload then? We already have | and &.
| BOOST_CHECK_EQUAL(reference_17.geometries, result_17->geometries); | ||
| BOOST_CHECK_EQUAL( | ||
| static_cast<bool>(result_2->annotations_type & RouteParameters::AnnotationsType::All), | ||
| true); |
There was a problem hiding this comment.
Same for all of these tests, type == All
oxidase
left a comment
There was a problem hiding this comment.
LGTM! I added some change requests, but they are minor
| } | ||
| // if header matches 'a:*', parse out the values for * | ||
| // and return in that header | ||
| headers.forEach((k, v) => { |
There was a problem hiding this comment.
👍 but lint complains about unused v variable
features/support/shared_steps.js
Outdated
| } | ||
| // if header matches 'a:*', parse out the values for * | ||
| // and return in that header | ||
| headers.forEach((k, v) => { |
|
|
||
| bool steps = false; | ||
| bool alternatives = false; | ||
| bool annotations = false; |
There was a problem hiding this comment.
this is now a duplication, better
bool annotations() const { return annotations_type != AnnotationsType::None; };
But it is not used in code base anymore, the best would be delete it and update unit tests.
There was a problem hiding this comment.
We can't do that - the annotations member attribute is in our public API - we can't change it to a function :(
|
|
||
| RouteParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_) | ||
| { | ||
| const auto add_annotation = [](engine::api::RouteParameters &route_parameters, |
There was a problem hiding this comment.
this lambda is not needed with operator |= and in-place phoenix bind (i added related comments)
There was a problem hiding this comment.
due to #3628 (comment) both members must be updated, so add_annotation must remain
| const boost::optional<bool> continue_straight_, | ||
| Args... args_) | ||
| : BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_}, | ||
| annotations_type{annotations_}, annotations{true}, geometries{geometries_}, |
There was a problem hiding this comment.
here annotations is true even if annotations_ is None
| overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1]); | ||
| overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1]) | | ||
| (qi::lit("annotations=") > | ||
| annotations_type[ph::bind(add_annotation, qi::_r1, qi::_1)] % ','); |
There was a problem hiding this comment.
... here in-place functor
annotations_type[ph::bind(&engine::api::RouteParameters::annotations_type, qi::_r1) |= qi::_1] % ',');
| static_cast<std::underlying_type_t<RouteParameters::AnnotationsType>>(lhs) | | ||
| static_cast<std::underlying_type_t<RouteParameters::AnnotationsType>>(rhs)); | ||
| } | ||
|
|
There was a problem hiding this comment.
... and here compound operator
inline RouteParameters::AnnotationsType& operator|=(RouteParameters::AnnotationsType& lhs,
RouteParameters::AnnotationsType rhs)
{
return lhs = lhs | rhs;
}
| engine::api::RouteParameters::AnnotationsType &route_param) { | ||
| route_parameters.annotations_type = route_parameters.annotations_type | route_param; | ||
| route_parameters.annotations = route_parameters.annotations_type != engine::api::RouteParameters::AnnotationsType::None; | ||
| return route_parameters; |
There was a problem hiding this comment.
returned value is not used and return can be removed
793ad55 to
58322d8
Compare
58322d8 to
9c14285
Compare
TheMarex
left a comment
There was a problem hiding this comment.
Code and test refactor looks great. 👍 Two thinks missing: Updating docs/http.md to reflect the new parameters and adding an entry to CHANGELOG.md.
|
This is still missing the Otherwise ready to merge. |
Issue
Closes #3563
Tasklist
Adapt annotations parsing for map matching routematching inherits from route