qabstractgrpcclient.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * MIT License
  3. *
  4. * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
  5. *
  6. * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy of this
  9. * software and associated documentation files (the "Software"), to deal in the Software
  10. * without restriction, including without limitation the rights to use, copy, modify,
  11. * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
  12. * to permit persons to whom the Software is furnished to do so, subject to the following
  13. * conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in all copies
  16. * or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  19. * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  20. * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  21. * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  22. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. * DEALINGS IN THE SOFTWARE.
  24. */
  25. #pragma once //QAbstractGrpcClient
  26. #include <memory>
  27. #include <functional>
  28. #include <type_traits>
  29. #include <QObject>
  30. #include <QPointer>
  31. #include <QByteArray>
  32. #include <qtprotobuflogging.h>
  33. #include <qabstractprotobufserializer.h>
  34. #include "qabstractgrpcchannel.h"
  35. #include "qtgrpcglobal.h"
  36. /*!
  37. * \defgroup QtGrpc
  38. * \brief Qt framework based gRPC clients and services
  39. */
  40. namespace QtProtobuf {
  41. class QGrpcAsyncReply;
  42. class QGrpcSubscription;
  43. class QGrpcAsyncOperationBase;
  44. class QAbstractGrpcChannel;
  45. class QAbstractGrpcClientPrivate;
  46. /*!
  47. * \private
  48. */
  49. using SubscriptionHandler = std::function<void(const QByteArray&)>;
  50. /*!
  51. * \ingroup QtGrpc
  52. * \brief The QAbstractGrpcClient class is bridge between gRPC clients and channels. QAbstractGrpcClient provides set of
  53. * bridge functions for client classes generated out of protobuf services.
  54. */
  55. class Q_GRPC_EXPORT QAbstractGrpcClient : public QObject
  56. {
  57. Q_OBJECT
  58. public:
  59. /*!
  60. * \brief Attaches \a channel to client as transport layer for gRPC. Parameters and return values will be serialized
  61. * to supported by channel format.
  62. * \see QAbstractGrcpChannel
  63. * \param channel Shared pointer to channel will be used as transport layer for gRPC
  64. */
  65. void attachChannel(const std::shared_ptr<QAbstractGrpcChannel> &channel);
  66. signals:
  67. /*!
  68. * \brief error signal is emited by client when error occured in channel or while serialization/deserialization
  69. * \param[out] code gRPC channel StatusCode
  70. * \param[out] errorText Error description from channel or from QGrpc
  71. */
  72. void error(const QGrpcStatus &status);
  73. protected:
  74. QAbstractGrpcClient(const QString &service, QObject *parent = nullptr);
  75. virtual ~QAbstractGrpcClient();
  76. /*!
  77. * \private
  78. * \brief Calls \p method of service client synchronously
  79. * \param[in] method Name of the method to be called
  80. * \param[in] arg Protobuf message argument for \p method
  81. * \param[out] ret A pointer to memory with protobuf message to write an gRPC reply to
  82. */
  83. template<typename A, typename R>
  84. QGrpcStatus call(const QString &method, const A &arg, const QPointer<R> &ret) {
  85. QGrpcStatus status{QGrpcStatus::Ok};
  86. if (ret.isNull()) {
  87. static const QString errorString("Unable to call method: %1. Pointer to return data is null");
  88. status = QGrpcStatus{QGrpcStatus::InvalidArgument, errorString.arg(method)};
  89. error(status);
  90. qProtoCritical() << errorString.arg(method);
  91. return status;
  92. }
  93. QByteArray retData;
  94. status = call(method, arg.serialize(serializer()), retData);
  95. if (status == QGrpcStatus::StatusCode::Ok) {
  96. return tryDeserialize(*ret, retData);
  97. }
  98. return status;
  99. }
  100. /*!
  101. * \private
  102. * \brief Calls \p method of service client asynchronously and returns pointer to assigned to call AsyncReply
  103. * \param[in] method Name of the method to be called
  104. * \param[in] arg Protobuf message argument for \p method
  105. */
  106. template<typename A>
  107. QGrpcAsyncReply *call(const QString &method, const A &arg) {
  108. return call(method, arg.serialize(serializer()));
  109. }
  110. /*!
  111. * \private
  112. * \brief Subscribes to message notifications from server-stream with given message argument \a arg
  113. * \param[in] method Name of the method to be called
  114. * \param[in] arg Protobuf message argument for \p method
  115. * \param[out] signal Callback with return-message as input parameter that will be called each time message
  116. * update recevied from server-stream
  117. */
  118. template<typename A>
  119. QGrpcSubscription *subscribe(const QString &method, const A &arg) {
  120. return subscribe(method, arg.serialize(serializer()));
  121. }
  122. /*!
  123. * \private
  124. * \brief Subscribes to message notifications from server-stream with given message argument \a arg
  125. * \param[in] method Name of the method to be called
  126. * \param[in] arg Protobuf message argument for \p method
  127. * \param[out] ret Pointer to preallocated return-message structure. \p ret Structure fields will be update each
  128. * time message update recevied from server-stream.
  129. * \note If \p ret is used as property-fields in other object, property NOTIFY signal won't be called in case of
  130. * updated message recevied from server-stream
  131. */
  132. template<typename A, typename R>
  133. QGrpcSubscription *subscribe(const QString &method, const A &arg, const QPointer<R> &ret) {
  134. if (ret.isNull()) {
  135. static const QString nullPointerError("Unable to subscribe method: %1. Pointer to return data is null");
  136. error({QGrpcStatus::InvalidArgument, nullPointerError.arg(method)});
  137. qProtoCritical() << nullPointerError.arg(method);
  138. return nullptr;
  139. }
  140. return subscribe(method, arg.serialize(serializer()), [ret, this](const QByteArray &data) {
  141. if (!ret.isNull()) {
  142. tryDeserialize(*ret, data);
  143. } else {
  144. static const QLatin1String nullPointerError("Pointer to return data is null while subscription update received");
  145. error({QGrpcStatus::InvalidArgument, nullPointerError});
  146. qProtoCritical() << nullPointerError;
  147. }
  148. });
  149. }
  150. /*!
  151. * \brief Canceles all subscriptions for specified \p method
  152. * \param[in] method Name of method subscription for to be canceled
  153. */
  154. void cancel(const QString &method);
  155. /*!
  156. * \brief serializer provides assigned to client serializer
  157. * \return pointer to serializer. Serializer is owned by QtProtobuf::QProtobufSerializerRegistry.
  158. */
  159. QAbstractProtobufSerializer *serializer() const;
  160. friend class QGrpcAsyncOperationBase;
  161. private:
  162. //!\private
  163. QGrpcStatus call(const QString &method, const QByteArray &arg, QByteArray &ret);
  164. //!\private
  165. QGrpcAsyncReply *call(const QString &method, const QByteArray &arg);
  166. //!\private
  167. QGrpcSubscription *subscribe(const QString &method, const QByteArray &arg, const QtProtobuf::SubscriptionHandler &handler = {});
  168. /*!
  169. * \private
  170. * \brief Deserialization helper
  171. */
  172. template<typename R>
  173. QGrpcStatus tryDeserialize(R &ret, const QByteArray &retData) {
  174. QGrpcStatus status{QGrpcStatus::Ok};
  175. try {
  176. ret.deserialize(serializer(), retData);
  177. } catch (std::invalid_argument &) {
  178. static const QLatin1String invalidArgumentErrorMessage("Response deserialization failed invalid field found");
  179. status = {QGrpcStatus::InvalidArgument, invalidArgumentErrorMessage};
  180. error(status);
  181. qProtoCritical() << invalidArgumentErrorMessage;
  182. } catch (std::out_of_range &) {
  183. static const QLatin1String outOfRangeErrorMessage("Invalid size of received buffer");
  184. status = {QGrpcStatus::OutOfRange, outOfRangeErrorMessage};
  185. error(status);
  186. qProtoCritical() << outOfRangeErrorMessage;
  187. } catch (...) {
  188. status = {QGrpcStatus::Internal, QLatin1String("Unknown exception caught during deserialization")};
  189. error(status);
  190. }
  191. return status;
  192. }
  193. Q_DISABLE_COPY_MOVE(QAbstractGrpcClient)
  194. std::unique_ptr<QAbstractGrpcClientPrivate> dPtr;
  195. };
  196. }