-
Notifications
You must be signed in to change notification settings - Fork 22
Create a ClientTrait for RustSDK #142
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
Create a ClientTrait for RustSDK #142
Conversation
ddbc538
to
bae6f53
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #142 +/- ##
==========================================
+ Coverage 59.97% 64.09% +4.12%
==========================================
Files 34 36 +2
Lines 15391 17550 +2159
==========================================
+ Hits 9230 11248 +2018
- Misses 6161 6302 +141 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
no functional test has been done yet.
None | ||
} | ||
}; | ||
let datapoint_entries = handle_get_metadata(vec![path], client).await.unwrap(); |
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.
Wouldn't simply unwrapping the result here crash the application in case of an err? might be a rare case though as this only happens when cli::print_error(...) or prin_resp_err_fm fails - might be neglectable.
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 it is neglectable because the error will be printed. If there is an error databroker-cli most problaly should panic because something went really wrong. Which will happen with unwrap()
df25af6
to
efb84d4
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.
No functional test has been done yet
@@ -41,6 +44,195 @@ pub enum ClientError { | |||
Function(Vec<Error>), | |||
} | |||
|
|||
#[async_trait] | |||
pub trait SDVClientTraitV1 { |
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.
So if I understand correctly, then we now have the following interfaces
ClientTrait
SDVClientTrait
=> SDVClientTraitV1
KuksaClientTrait
=> KuksaClientTraitV1
=> KuksaClientTraitV2
I guess we only need to expose / use SdvClientTrait and KuksaClientTrait for our own purposes (e.g. easy extension and use in databroker-cli?)
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.
If we now add a new protocol, e.g. kuksa.val.v3 we would need to add a new KuksaClientTraitV3, and adapt the conversions thingy to convert the proto specific models to the alias classes, right?
lib/common/src/types.rs
Outdated
pub type SensorUpdateSDVTypeV1 = HashMap<String, SDVprotoV1::Datapoint>; | ||
pub type UpdateActuationSDVTypeV1 = HashMap<String, SDVprotoV1::Datapoint>; | ||
pub type PathSDVTypeV1 = Vec<String>; | ||
pub type SubscribeSDVTypeV1 = String; | ||
pub type PublishResponseSDVTypeV1 = SDVprotoV1::UpdateDatapointsReply; | ||
pub type GetResponseSDVTypeV1 = HashMap<String, SDVprotoV1::Datapoint>; | ||
pub type SubscribeResponseSDVTypeV1 = Streaming<SDVprotoV1::SubscribeReply>; | ||
pub type ProvideResponseSDVTypeV1 = Streaming<SDVprotoV1::SubscribeReply>; | ||
pub type ActuateResponseSDVTypeV1 = SDVprotoV1::SetDatapointsReply; | ||
pub type MetadataResponseSDVTypeV1 = Vec<SDVprotoV1::Metadata>; | ||
|
||
// Type aliases V1 | ||
pub type SensorUpdateTypeV1 = HashMap<String, protoV1::Datapoint>; | ||
pub type UpdateActuationTypeV1 = HashMap<String, protoV1::Datapoint>; | ||
pub type PathTypeV1 = Vec<String>; | ||
pub type SubscribeTypeV1 = PathTypeV1; | ||
pub type PublishResponseTypeV1 = (); | ||
pub type GetResponseTypeV1 = Vec<protoV1::DataEntry>; | ||
pub type SubscribeResponseTypeV1 = Streaming<protoV1::SubscribeResponse>; | ||
pub type ProvideResponseTypeV1 = Streaming<protoV1::SubscribeResponse>; | ||
pub type ActuateResponseTypeV1 = (); | ||
pub type MetadataResponseTypeV1 = GetResponseTypeV1; | ||
|
||
// Type aliases V2 | ||
pub type SensorUpdateTypeV2 = protoV2::Value; | ||
pub type UpdateActuationTypeV2 = SensorUpdateTypeV2; | ||
pub type UpdateActuationsTypeV2 = HashMap<PathTypeV2, UpdateActuationTypeV2>; | ||
pub type PathTypeV2 = String; | ||
pub type PathsTypeV2 = Vec<PathTypeV2>; | ||
pub type IdsTypeV2 = Vec<i32>; | ||
pub type SubscribeTypeV2 = PathsTypeV2; | ||
pub type SubscribeByIdTypeV2 = IdsTypeV2; | ||
pub type PublishResponseTypeV2 = (); | ||
pub type GetResponseTypeV2 = protoV2::Datapoint; | ||
pub type MultipleGetResponseTypeV2 = Vec<GetResponseTypeV2>; | ||
pub type SubscribeResponseTypeV2 = tonic::Streaming<protoV2::SubscribeResponse>; | ||
pub type SubscribeByIdResponseTypeV2 = tonic::Streaming<protoV2::SubscribeByIdResponse>; | ||
pub type ProvideResponseTypeV2 = (); | ||
pub type ActuateResponseTypeV2 = (); | ||
pub type OpenProviderStreamResponseTypeV2 = OpenProviderStream; | ||
pub type MetadataTypeV2 = (PathTypeV2, PathTypeV2); | ||
pub type MetadataResponseTypeV2 = Vec<protoV2::Metadata>; | ||
pub type ServerInfoTypeV2 = ServerInfo; |
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.
Still feels pretty arbitrary. It's probably best to add some comments to understand the overall strategy here to simplify on how to decide which types to assign? Any granularity that helps me to decide which types to use when we add a new protocol and therefore a new Trait is sufficient.
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.
hmm, think of it more to handle the conversions and now what you want to do there. i know it feels arbitrary. One could argue you can just use the same naming as in the API's. Thats what I thought about but was not sure this does not suggest then something different. So for example publish_value
have a PublishValueRequestType
and PublishValueResponseType
same for the other functions - XRequestType
XResponseType
. Would that make more sense to you? I introduced PathType
because I felt like every underlying function should handle the input path as the same in ONE ClientTrait however it could differ BETWEEN Traits.
lib/kuksa_val_v2/src/lib.rs
Outdated
vss_paths: Vec<&str>, | ||
vss_paths: Vec<String>, |
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.
Could you elaborate on this change? Is it better to use a "String" over a "&str"?
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.
That's what I'm currently working on. The problem with references in rust and traits seems to be that lifetime handling is difficult. So I'm currently trying to solve this by using clones and hence no references. But I'm extending my horizon and am experimenting how it would be done with lifetime handling.
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 experimented a little bit with references and lifetimes. Could not bring it in a solid state yet https://github.com/boschglobal/kuksa-databroker/tree/experiment/client_trait_refs_lifetimes so will continue fixing review findings due to the lack of time.
Ok(ClientTraitV2::subscribe(self, paths.convert_to_v2(), None) | ||
.await | ||
.unwrap() | ||
.convert_to_v1()) |
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.
will panic
Ok(ClientTraitV2::subscribe(self, paths.convert_to_v2(), None) | ||
.await | ||
.unwrap() | ||
.convert_to_v1()) |
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.
will panic
|
||
Ok(metadata_result) | ||
#[async_trait] | ||
impl kuksa_common::SDVClientTraitV1 for KuksaClient { |
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.
Asking myself here, should the KuksaClient (val.v1) really implement the SDVClientTrait? If there are similar methods I would probably re-declare them in the corresponding interface instead of "implementing all SDV stuff" where we potentially inherit a lot of unimplemented / unsupported / incompatible stuff.
The more unimplemented / incompatible stuff is exposed on a general level, the less simple and useful is the abstraction.
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 agree, that was just the first attempt to keep backwards compatibility. If the interface is deprecated this would be changed to a deprecation message.
|
||
impl ConvertToV1<SubscribeResponseTypeV1> for SubscribeResponseTypeV2 { | ||
fn convert_to_v1(self) -> SubscribeResponseTypeV1 { | ||
unimplemented!("Not possible to convert stream objects!") |
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.
well, it actually is possible isn't it? read the messages from stream, convert them to a v1 object and then emitting them to a new stream. compare to databroker/kuksa_val_v1/val.rs -> convert_to_proto_stream but yeah I admit, that whatever relates to streaming stuff feels really overcomplicated in Rust.
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.
Functional Test:
- could not test kuksa.val.v2, because it's not yet supported by databroker-cli
- kuksa.val.v1 worked
sdv.databroker.v1 returned unimplemented for everything, but this issue already seems to persist on main?works. I forgot to put the --enable-databroker-v1 flag
with regard to functional testing. For |
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
For handling the Rust SDK and preparing to make it an own crate this introduces a
ClientTrait
. With this the maintenance of the soon-to-be Rust SDK will be easy and manageable.