-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
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 | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the changes here?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
include/engine/api/route_api.hpp
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm the ~100 lines above are all doing the same:
- check if annotation x was requested
- create
json::Array
for many x - extract
step.x
fromRouteStep
and 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
RouteStep
and returning annotations items out of it (e.g.step.duration
forAnnotationsType::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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 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.
@@ -67,6 +67,16 @@ struct RouteParameters : public BaseParameters | |||
Full, | |||
False | |||
}; | |||
enum class AnnotationsType : int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is an empty list allowed by this? (I don't know => make a unit test)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here it can start from 1<<0
, but better to use explicit numerals 0x01, 0x02, ...
54721eb
to
47a02df
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
return cb(new Error('Annotation not found in response', a_type)); | ||
got[k] = annotation[a_type]; | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 };
@@ -128,6 +132,9 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) | |||
CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses); | |||
CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates); | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for all of these tests, type == All
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 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) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here v
is unused
Args... args_) | ||
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_}, | ||
annotations_type{annotations_}, annotations{true}, geometries{geometries_}, | ||
overview{overview_}, continue_straight{continue_straight_} | ||
{ | ||
} | ||
|
||
bool steps = false; | ||
bool alternatives = false; | ||
bool annotations = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't do that - the annotations member attribute is in our public API - we can't change it to a function :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -42,6 +42,13 @@ struct RouteParametersGrammar : public BaseParametersGrammar<Iterator, Signature | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this lambda is not needed with operator |=
and in-place phoenix bind (i added related comments)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
returned value is not used and return
can be removed
793ad55
to
58322d8
Compare
58322d8
to
9c14285
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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