Viam C++ SDK current
Loading...
Searching...
No Matches
client_helper.hpp
1#pragma once
2
3#include <boost/optional.hpp>
4
6#include <viam/sdk/common/grpc_fwd.hpp>
7#include <viam/sdk/common/proto_value.hpp>
8#include <viam/sdk/rpc/dial.hpp>
9
10namespace viam {
11namespace sdk {
12
13namespace client_helper_details {
14
15[[noreturn]] void errorHandlerReturnedUnexpectedly(const ::grpc::Status*) noexcept;
16
17// Helper function to test equality of status with grpc::StatusCode::CANCELLED.
18bool isStatusCancelled(int status) noexcept;
19
20// Set the mutable name of a request to the client name.
21// This function only participates in overload resolution if the request has a mutable_name method.
22template <typename RequestType,
23 typename ClientType,
24 typename = decltype(&RequestType::mutable_name)>
25void set_name(RequestType* req, const ClientType* client) {
26 *req->mutable_name() = client->name();
27}
28
29// No-op version of set_name above. This overload is only selected if the request type does not have
30// a mutable_name field.
31void set_name(...);
32
33boost::optional<std::string> debug_map_value(const ProtoStruct& extra);
34
35} // namespace client_helper_details
36
37// the authority on a grpc::ClientContext is sometimes set to an invalid uri on mac, causing
38// `rust-utils` to fail to process gRPC requests. This class provides a convenience wrapper around a
39// grpc ClientContext that allows us to make any necessary modifications to authority or else where
40// to avoid runtime issues.
41// For more details, see https://viam.atlassian.net/browse/RSDK-5194.
43 public:
45 ClientContext(const ViamChannel& channel);
46
48
49 void try_cancel();
50
51 operator GrpcClientContext*();
52 operator const GrpcClientContext*() const;
53
54 void set_debug_key(const std::string& debug_key);
55
56 private:
57 void set_client_ctx_authority_();
58 void add_viam_client_version_();
59 std::unique_ptr<GrpcClientContext> wrapped_context_;
60};
61
62// Method type for a gRPC call that returns a response message type.
63template <typename StubType, typename RequestType, typename ResponseType>
64using SyncMethodType = ::grpc::Status (StubType::*)(GrpcClientContext*,
65 const RequestType&,
66 ResponseType*);
67
68// Method type for a gRPC call that returns a stream of response message type.
69template <typename StubType, typename RequestType, typename ResponseType>
70using StreamingMethodType = std::unique_ptr<GrpcClientReaderInterface<ResponseType>> (StubType::*)(
71 GrpcClientContext*, const RequestType&);
72
73template <typename ClientType,
74 typename StubType,
75 typename RequestType,
76 typename ResponseType,
77 typename MethodType>
79 static void default_rsc_(RequestType&) {}
80 static void default_rhc_(const ResponseType&) {}
81 static void default_ehc_(const ::grpc::Status* status) {
82 throw GRPCException(status);
83 }
84
85 public:
86 explicit ClientHelper(ClientType* client, StubType* stub, MethodType pfn)
87 : client_(client), stub_(stub), pfn_(pfn) {}
88
89 ClientHelper& with(const ProtoStruct& extra) {
90 return with(extra, default_rsc_);
91 }
92
93 template <typename RequestSetupCallable>
94 ClientHelper& with(RequestSetupCallable&& rsc) {
95 std::forward<RequestSetupCallable>(rsc)(request_);
96 return *this;
97 }
98
99 template <typename RequestSetupCallable>
100 ClientHelper& with(const ProtoStruct& extra, RequestSetupCallable&& rsc) {
101 if (auto val = client_helper_details::debug_map_value(extra)) {
102 debug_key_ = std::move(*val);
103 }
104
105 proto_convert_details::to_proto_impl<ProtoStruct>{}(extra, request_.mutable_extra());
106 return with(std::forward<RequestSetupCallable>(rsc));
107 }
108
109 template <typename ResponseHandlerCallable = decltype(default_rhc_)>
110 auto invoke(ResponseHandlerCallable&& rhc = default_rhc_) {
111 return invoke(std::forward<ResponseHandlerCallable>(rhc), default_ehc_);
112 }
113
114 template <typename ResponseHandlerCallable, typename ErrorHandlerCallable>
115 auto invoke(ResponseHandlerCallable&& rhc, ErrorHandlerCallable&& ehc) {
116 client_helper_details::set_name(&request_, client_);
117 ClientContext ctx(client_->channel());
118
119 if (debug_key_ != "") {
120 ctx.set_debug_key(debug_key_);
121 }
122 const auto result = (stub_->*pfn_)(ctx, request_, &response_);
123 if (result.ok()) {
124 return std::forward<ResponseHandlerCallable>(rhc)(
125 const_cast<const ResponseType&>(response_));
126 }
127
128 std::forward<ErrorHandlerCallable>(ehc)(&result);
129 client_helper_details::errorHandlerReturnedUnexpectedly(&result);
130 }
131
132 // A version of invoke for gRPC calls returning `(stream ResponseType)`.
133 // ResponseHandlerCallable will be called for every response in the reader, and should return
134 // false to indicate it is no longer interested in the stream.
135 template <typename ResponseHandlerCallable,
136 typename ErrorHandlerCallable = decltype(default_ehc_)>
137 auto invoke_stream(ResponseHandlerCallable rhc, ErrorHandlerCallable&& ehc = default_ehc_) {
138 *request_.mutable_name() = client_->name();
139 ClientContext ctx(client_->channel());
140
141 auto reader = (stub_->*pfn_)(ctx, request_);
142
143 bool cancelled_by_handler = false;
144
145 while (reader->Read(&response_)) {
146 if (!rhc(response_)) {
147 cancelled_by_handler = true;
148 ctx.try_cancel();
149 break;
150 }
151 }
152
153 const auto result = reader->Finish();
154
155 if (result.ok() || (cancelled_by_handler &&
156 client_helper_details::isStatusCancelled(result.error_code()))) {
157 return;
158 }
159
160 std::forward<ErrorHandlerCallable>(ehc)(&result);
161 client_helper_details::errorHandlerReturnedUnexpectedly(&result);
162 }
163
164 private:
165 ClientType* client_;
166 StubType* stub_;
167 std::string debug_key_;
168 MethodType pfn_;
169 RequestType request_;
170 ResponseType response_;
171};
172
173template <typename ClientType, typename StubType, typename RequestType, typename ResponseType>
174auto make_client_helper(ClientType* client,
175 StubType& stub,
176 SyncMethodType<StubType, RequestType, ResponseType> method) {
177 return ClientHelper<ClientType,
178 StubType,
179 RequestType,
180 ResponseType,
181 SyncMethodType<StubType, RequestType, ResponseType>>(client, &stub, method);
182}
183
184template <typename ClientType, typename StubType, typename RequestType, typename ResponseType>
185auto make_client_helper(ClientType* client,
186 StubType& stub,
187 StreamingMethodType<StubType, RequestType, ResponseType> method) {
188 return ClientHelper<ClientType,
189 StubType,
190 RequestType,
191 ResponseType,
192 StreamingMethodType<StubType, RequestType, ResponseType>>(
193 client, &stub, method);
194}
195
196} // namespace sdk
197} // namespace viam
Definition client_helper.hpp:42
Definition client_helper.hpp:78
Defines an exception from a gRPC interaction.
Definition exception.hpp:57
Definition dial.hpp:25
Defines custom exceptions for the SDK.