qabstractgrpcclient.h 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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 <QByteArray>
  31. #include <qtprotobuflogging.h>
  32. #include <qabstractprotobufserializer.h>
  33. #include "qabstractgrpcchannel.h"
  34. #include "qtgrpcglobal.h"
  35. namespace QtProtobuf {
  36. class QGrpcAsyncReply;
  37. class QAbstractGrpcChannel;
  38. class QAbstractGrpcClientPrivate;
  39. /*!
  40. * \ingroup QtGrpc
  41. * \brief The QAbstractGrpcClient class is bridge between gRPC clients and channels. QAbstractGrpcClient provides set of
  42. * bridge functions for client classes generated out of protobuf services.
  43. */
  44. class Q_GRPC_EXPORT QAbstractGrpcClient : public QObject
  45. {
  46. Q_OBJECT
  47. public:
  48. /*!
  49. * \brief Attaches \a channel to client as transport layer for gRPC. Parameters and return values will be serialized
  50. * to supported by channel format.
  51. * \see QAbstractGrcpChannel
  52. * \param channel Shared pointer to channel will be used as transport layer for gRPC
  53. */
  54. void attachChannel(const std::shared_ptr<QAbstractGrpcChannel> &channel);
  55. signals:
  56. /*!
  57. * \brief error signal is emited by client when error occured in channel or while serialization/deserialization
  58. * \param[out] code gRPC channel StatusCode
  59. * \param[out] errorText Error description from channel or from QGrpc
  60. */
  61. void error(const QGrpcStatus &status);
  62. protected:
  63. QAbstractGrpcClient(const QString &service, QObject *parent = nullptr);
  64. virtual ~QAbstractGrpcClient();
  65. /*!
  66. * \private
  67. * \brief Calls \p method of service client synchronously
  68. * \param[in] method Name of the method to be called
  69. * \param[in] arg Protobuf message argument for \p method
  70. * \param[out] ret A pointer to memory with protobuf message to write an gRPC reply to
  71. */
  72. template<typename A, typename R>
  73. QGrpcStatus call(const QString &method, const A &arg, const QPointer<R> &ret) {
  74. QGrpcStatus status{QGrpcStatus::Ok};
  75. if (ret.isNull()) {
  76. static const QString errorString("Unable to call method: %1. Pointer to return data is null");
  77. status = QGrpcStatus{QGrpcStatus::InvalidArgument, errorString.arg(method)};
  78. error(status);
  79. qProtoCritical() << errorString.arg(method);
  80. return status;
  81. }
  82. QByteArray retData;
  83. status = call(method, arg.serialize(serializer()), retData);
  84. if (status == QGrpcStatus::StatusCode::Ok) {
  85. return tryDeserialize(*ret, retData);
  86. }
  87. return status;
  88. }
  89. /*!
  90. * \private
  91. * \brief Calls \p method of service client asynchronously and returns pointer to assigned to call AsyncReply
  92. * \param[in] method Name of the method to be called
  93. * \param[in] arg Protobuf message argument for \p method
  94. */
  95. template<typename A>
  96. QGrpcAsyncReply *call(const QString &method, const A &arg) {
  97. return call(method, arg.serialize(serializer()));
  98. }
  99. /*!
  100. * \private
  101. * \brief Subscribes to message notifications from server-stream with given message argument \a arg
  102. * \param[in] method Name of the method to be called
  103. * \param[in] arg Protobuf message argument for \p method
  104. * \param[out] signal Callback with return-message as input parameter that will be called each time message
  105. * update recevied from server-stream
  106. */
  107. template<typename A, typename R, typename C,
  108. typename std::enable_if_t<std::is_base_of<QAbstractGrpcClient, C>::value, int> = 0>
  109. void subscribe(const QString &method, const A &arg, void(C::*signal)(const R &)) {
  110. subscribe(method, arg.serialize(serializer()), [this, signal](const QByteArray &data) {
  111. R ret;
  112. tryDeserialize(ret, data);
  113. C *client = static_cast<C *>(this);
  114. (client->*signal)(ret);
  115. });
  116. }
  117. /*!
  118. * \private
  119. * \brief Subscribes to message notifications from server-stream with given message argument \a arg
  120. * \param[in] method Name of the method to be called
  121. * \param[in] arg Protobuf message argument for \p method
  122. * \param[out] ret Pointer to preallocated return-message structure. \p ret Structure fields will be update each
  123. * time message update recevied from server-stream.
  124. * \note If \p ret is used as property-fiels in other object, property NOTIFY signal won't be called in case of
  125. * updated message recevied from server-stream
  126. */
  127. template<typename A, typename R>
  128. void subscribe(const QString &method, const A &arg, const QPointer<R> &ret) {
  129. if (ret.isNull()) {
  130. static const QString nullPointerError("Unable to subscribe method: %1. Pointer to return data is null");
  131. error({QGrpcStatus::InvalidArgument, nullPointerError.arg(method)});
  132. qProtoCritical() << nullPointerError.arg(method);
  133. return;
  134. }
  135. subscribe(method, arg.serialize(serializer()), [ret, this](const QByteArray &data) {
  136. if (!ret.isNull()) {
  137. tryDeserialize(*ret, data);
  138. } else {
  139. static const QLatin1String nullPointerError("Pointer to return data is null while subscription update received");
  140. error({QGrpcStatus::InvalidArgument, nullPointerError});
  141. qProtoCritical() << nullPointerError;
  142. }
  143. });
  144. }
  145. QAbstractProtobufSerializer *serializer() const;
  146. friend class QGrpcAsyncReply;
  147. private:
  148. /*!
  149. * \private
  150. */
  151. QGrpcStatus call(const QString &method, const QByteArray &arg, QByteArray &ret);
  152. /*!
  153. * \private
  154. */
  155. QGrpcAsyncReply *call(const QString &method, const QByteArray &arg);
  156. /*!
  157. * \private
  158. */
  159. void subscribe(const QString &method, const QByteArray &arg, const std::function<void(const QByteArray &)> &handler);
  160. /*!
  161. * \private
  162. * \brief Deserialization helper
  163. */
  164. template<typename R>
  165. QGrpcStatus tryDeserialize(R &ret, const QByteArray &retData) {
  166. QGrpcStatus status{QGrpcStatus::Ok};
  167. try {
  168. ret.deserialize(serializer(), retData);
  169. } catch (std::invalid_argument &) {
  170. static const QLatin1String invalidArgumentErrorMessage("Response deserialization failed invalid field found");
  171. status = {QGrpcStatus::InvalidArgument, invalidArgumentErrorMessage};
  172. error(status);
  173. qProtoCritical() << invalidArgumentErrorMessage;
  174. } catch (std::out_of_range &) {
  175. static const QLatin1String outOfRangeErrorMessage("Invalid size of received buffer");
  176. status = {QGrpcStatus::OutOfRange, outOfRangeErrorMessage};
  177. error(status);
  178. qProtoCritical() << outOfRangeErrorMessage;
  179. } catch (...) {
  180. status = {QGrpcStatus::Internal, QLatin1String("Unknown exception caught during deserialization")};
  181. error(status);
  182. }
  183. return status;
  184. }
  185. Q_DISABLE_COPY_MOVE(QAbstractGrpcClient)
  186. std::unique_ptr<QAbstractGrpcClientPrivate> d_ptr;
  187. };
  188. }