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