1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include <QtGrpc/private/qgrpcoperation_p.h>
6#include <QtGrpc/private/qtgrpclogging_p.h>
7#include <QtGrpc/qgrpcoperation.h>
8#include <QtGrpc/qgrpcoperationcontext.h>
9
10#include <QtProtobuf/private/qprotobufmessage_p.h>
11
12#include <QtCore/qbytearray.h>
13#include <QtCore/qeventloop.h>
14#include <QtCore/qpointer.h>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20QGrpcOperationPrivate::~QGrpcOperationPrivate()
21 = default;
22
23/*!
24 \class QGrpcOperation
25 \inmodule QtGrpc
26 \brief The QGrpcOperation class provides common operations to handle the
27 \gRPC communication from the client side.
28
29 QGrpcOperation serves as the base class for the four \gRPC method types:
30 QGrpcCallReply (unary calls), QGrpcServerStream (server streaming),
31 QGrpcClientStream (client streaming), and QGrpcBidiStream (bidirectional
32 streaming). It provides a common interface for interacting with these
33 remote procedure calls (RPCs).
34
35 Each QGrpcOperation corresponds to a specific RPC requested through the
36 generated client interface.
37
38 For a high-level overview, refer to the \l {clientguide} {Qt GRPC
39 Client Guide}.
40*/
41
42/*!
43 \fn void QGrpcOperation::finished(const QGrpcStatus &status)
44
45 This signal is emitted when a previously started RPC has finished. The \a
46 status provides additional information about the outcome of the RPC.
47
48 After this signal is received, no further write or read operations should
49 be performed on the operation object. At this point, it is safe to reuse or
50 destroy the RPC object.
51
52 \note This signal is emitted only once, and in most cases, you will want to
53 disconnect right after receiving it to avoid issues, such as lambda
54 captures not being destroyed after receiving the signal. An easy way to
55 achieve this is by using the \l {Qt::} {SingleShotConnection} connection
56 type. See \l {Single Shot RPCs} for further details.
57*/
58
59/*!
60 \internal
61
62 Constructs a QGrpcOperation using \a operationContext to communicate
63 with the underlying channel and sets \a parent as the owner.
64*/
65QGrpcOperation::QGrpcOperation(std::shared_ptr<QGrpcOperationContext> operationContext,
66 QObject *parent)
67 : QObject(*new QGrpcOperationPrivate(std::move(operationContext)), parent)
68{
69 Q_D(QGrpcOperation);
70 [[maybe_unused]] bool valid = QObject::connect(sender: d->operationContext.get(),
71 signal: &QGrpcOperationContext::messageReceived, context: this,
72 slot: [this](const QByteArray &data) {
73 Q_D(QGrpcOperation);
74 d->data = data;
75 });
76 Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
77 "Unable to make connection to the 'messageReceived' signal");
78
79 valid = QObject::connect(sender: d->operationContext.get(), signal: &QGrpcOperationContext::finished, context: this,
80 slot: [this](const QGrpcStatus &status) {
81 if (!isFinished()) {
82 Q_D(QGrpcOperation);
83 d->isFinished.storeRelaxed(newValue: true);
84 emit this->finished(status);
85 }
86 });
87 Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
88 "Unable to make connection to the 'finished' signal");
89}
90
91/*!
92 Destroys the QGrpcOperation.
93*/
94QGrpcOperation::~QGrpcOperation() = default;
95
96/*!
97 \fn template <typename T, QtProtobuf::if_protobuf_message<T> = true> std::optional<T> QGrpcOperation::read() const
98
99 Reads a message from a raw byte array stored within this operation object.
100
101 Returns an optional deserialized message. On failure, \c {std::nullopt} is
102 returned.
103
104 \note This function only participates in overload resolution if \c T is a
105 subclass of QProtobufMessage.
106
107 \sa read(QProtobufMessage *)
108*/
109
110/*!
111 \since 6.8
112
113 Reads a message from a raw byte array stored within this operation object.
114
115 The function writes the deserialized value to the \a message pointer.
116
117 If the deserialization is successful, this function returns \c true.
118 Otherwise, it returns \c false.
119
120 \sa read()
121*/
122bool QGrpcOperation::read(QProtobufMessage *message) const
123{
124 Q_ASSERT_X(message != nullptr, "QGrpcOperation::read",
125 "Can't read to nullptr QProtobufMessage");
126 Q_D(const QGrpcOperation);
127 const auto ser = d->operationContext->serializer();
128 Q_ASSERT_X(ser, "QGrpcOperation", "The serializer is null");
129
130 if (auto responseMetaType = d->operationContext->responseMetaType(); responseMetaType.isValid()
131 && QProtobufMessagePrivate::get(message)->metaObject != responseMetaType.metaObject()) {
132 qGrpcWarning("Operation result meta type doesn't match the message meta type.");
133 }
134
135 if (!ser->deserialize(message, data: d->data)) {
136 qGrpcWarning() << "Unable to deserialize message(" << qToUnderlying(e: ser->lastError()) <<"): "
137 << ser->lastErrorString();
138 return false;
139 }
140 return true;
141}
142
143/*!
144 Tries to cancel the RPC immediately. Successful cancellation cannot be
145 guaranteed. Emits the \l finished signal with a \l {QtGrpc::StatusCode::}
146 {Cancelled} status code.
147
148 \sa QGrpcOperationContext::cancelRequested
149*/
150void QGrpcOperation::cancel()
151{
152 if (isFinished())
153 return;
154 Q_D(QGrpcOperation);
155 emit d->operationContext->cancelRequested();
156}
157
158#if QT_DEPRECATED_SINCE(6, 13)
159
160/*!
161 \deprecated [6.13] Use serverInitialMetadata() and serverTrailingMetadata() instead.
162
163 \include qgrpcoperation.cpp serverInitialMetadata
164
165 \sa serverInitialMetadata() serverTrailingMetadata()
166*/
167const QHash<QByteArray, QByteArray> &QGrpcOperation::metadata() const & noexcept
168{
169 Q_D(const QGrpcOperation);
170 QT_IGNORE_DEPRECATIONS(return d->operationContext->serverMetadata();)
171}
172
173#endif // QT_DEPRECATED_SINCE(6, 13)
174
175/*!
176 \since 6.10
177
178//! [serverInitialMetadata]
179 Returns the initial metadata received from the server before any response
180 messages.
181
182 Initial metadata is sent by the server immediately after the call is
183 established. It may include key-value pairs that provide context for the
184 call.
185
186 \include qtgrpc-shared.qdocinc http2-metadata-note
187//! [serverInitialMetadata]
188
189 The metadata may contain multiple entries under the same key.
190
191 \sa serverInitialMetadataReceived() serverTrailingMetadata()
192*/
193const QMultiHash<QByteArray, QByteArray> &QGrpcOperation::serverInitialMetadata() const & noexcept
194{
195 Q_D(const QGrpcOperation);
196 return d->operationContext->serverInitialMetadata();
197}
198
199/*!
200 \since 6.10
201
202//! [serverTrailingMetadata]
203 Returns the trailing metadata received from the server after all response
204 messages.
205
206 Trailing metadata is sent only by the server once all response messages
207 have been sent and just before the RPC completes. It may include key-value
208 pairs providing additional context about the completed call.
209
210 \include qtgrpc-shared.qdocinc http2-metadata-note
211//! [serverTrailingMetadata]
212
213 The metadata may contain multiple entries under the same key.
214
215 \sa serverInitialMetadata()
216*/
217const QMultiHash<QByteArray, QByteArray> &QGrpcOperation::serverTrailingMetadata() const & noexcept
218{
219 Q_D(const QGrpcOperation);
220 return d->operationContext->serverTrailingMetadata();
221}
222
223/*!
224 Returns the method name associated with this RPC operation.
225*/
226QLatin1StringView QGrpcOperation::method() const noexcept
227{
228 Q_D(const QGrpcOperation);
229 return d->operationContext->method();
230}
231
232/*!
233 Returns true if this operation has finished, meaning that no more
234 operations can happen on the corresponding RPC, otherwise returns false.
235*/
236bool QGrpcOperation::isFinished() const noexcept
237{
238 Q_D(const QGrpcOperation);
239 return d->isFinished.loadRelaxed();
240}
241
242/*!
243 \internal
244 \fn const QGrpcOperationContext &QGrpcOperation::context() const &
245 \fn QGrpcOperationContext &QGrpcOperation::context() &
246
247 Returns a reference to the internal operation context.
248*/
249const QGrpcOperationContext &QGrpcOperation::context() const & noexcept
250{
251 Q_D(const QGrpcOperation);
252 return *d->operationContext;
253}
254
255bool QGrpcOperation::event(QEvent *event)
256{
257 return QObject::event(event);
258}
259
260/*!
261 Returns the meta type of the RPC response message.
262 */
263QMetaType QGrpcOperation::responseMetaType() const
264{
265 Q_D(const QGrpcOperation);
266 return d->operationContext->responseMetaType();
267}
268
269QT_END_NAMESPACE
270
271#include "moc_qgrpcoperation.cpp"
272

source code of qtgrpc/src/grpc/qgrpcoperation.cpp