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/qtgrpclogging_p.h>
6#include <QtGrpc/qgrpcoperation.h>
7#include <QtGrpc/qgrpcoperationcontext.h>
8
9#include <QtCore/private/qobject_p.h>
10#include <QtCore/qatomic.h>
11#include <QtCore/qbytearray.h>
12#include <QtCore/qeventloop.h>
13#include <QtCore/qpointer.h>
14
15QT_BEGIN_NAMESPACE
16
17using namespace Qt::StringLiterals;
18
19/*!
20 \class QGrpcOperation
21 \inmodule QtGrpc
22 \brief The QGrpcOperation class provides common operations to handle the
23 \gRPC communication from the client side.
24
25 QGrpcOperation serves as the base class for the four \gRPC method types:
26 QGrpcCallReply (unary calls), QGrpcServerStream (server streaming),
27 QGrpcClientStream (client streaming), and QGrpcBidiStream (bidirectional
28 streaming). It provides a common interface for interacting with these
29 remote procedure calls (RPCs).
30
31 Each QGrpcOperation corresponds to a specific RPC requested through the
32 generated client interface.
33
34 For a high-level overview, refer to the \l {clientguide} {Qt GRPC
35 Client Guide}.
36*/
37
38/*!
39 \fn void QGrpcOperation::finished(const QGrpcStatus &status)
40
41 This signal is emitted when a previously started RPC has finished. The \a
42 status provides additional information about the outcome of the RPC.
43
44 After this signal is received, no further write or read operations should
45 be performed on the operation object. At this point, it is safe to reuse or
46 destroy the RPC object.
47
48 \note This signal is emitted only once, and in most cases, you will want to
49 disconnect right after receiving it to avoid issues, such as lambda
50 captures not being destroyed after receiving the signal. An easy way to
51 achieve this is by using the \l {Qt::} {SingleShotConnection} connection
52 type. See \l {Single Shot RPCs} for further details.
53*/
54
55class QGrpcOperationPrivate : public QObjectPrivate
56{
57 Q_DECLARE_PUBLIC(QGrpcOperation)
58public:
59 explicit QGrpcOperationPrivate(std::shared_ptr<QGrpcOperationContext> &&operationContext_)
60 : operationContext(operationContext_)
61 {
62 }
63
64 QByteArray data;
65 std::shared_ptr<QGrpcOperationContext> operationContext;
66 QAtomicInteger<bool> isFinished{ false };
67};
68
69/*!
70 \internal
71
72 Constructs a QGrpcOperation using \a operationContext to communicate
73 with the underlying channel and sets \a parent as the owner.
74*/
75QGrpcOperation::QGrpcOperation(std::shared_ptr<QGrpcOperationContext> operationContext,
76 QObject *parent)
77 : QObject(*new QGrpcOperationPrivate(std::move(operationContext)), parent)
78{
79 Q_D(QGrpcOperation);
80 [[maybe_unused]] bool valid = QObject::connect(sender: d->operationContext.get(),
81 signal: &QGrpcOperationContext::messageReceived, context: this,
82 slot: [this](const QByteArray &data) {
83 Q_D(QGrpcOperation);
84 d->data = data;
85 });
86 Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
87 "Unable to make connection to the 'messageReceived' signal");
88
89 valid = QObject::connect(sender: d->operationContext.get(), signal: &QGrpcOperationContext::finished, context: this,
90 slot: [this](const QGrpcStatus &status) {
91 if (!isFinished()) {
92 Q_D(QGrpcOperation);
93 d->isFinished.storeRelaxed(newValue: true);
94 emit this->finished(status);
95 }
96 });
97 Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
98 "Unable to make connection to the 'finished' signal");
99}
100
101/*!
102 Destroys the QGrpcOperation.
103*/
104QGrpcOperation::~QGrpcOperation() = default;
105
106/*!
107 \fn template <typename T, QtProtobuf::if_protobuf_message<T> = true> std::optional<T> QGrpcOperation::read() const
108
109 Reads a message from a raw byte array stored within this operation object.
110
111 Returns an optional deserialized message. On failure, \c {std::nullopt} is
112 returned.
113
114 \note This function only participates in overload resolution if \c T is a
115 subclass of QProtobufMessage.
116
117 \sa read(QProtobufMessage *)
118*/
119
120/*!
121 \since 6.8
122
123 Reads a message from a raw byte array stored within this operation object.
124
125 The function writes the deserialized value to the \a message pointer.
126
127 If the deserialization is successful, this function returns \c true.
128 Otherwise, it returns \c false.
129
130 \sa read()
131*/
132bool QGrpcOperation::read(QProtobufMessage *message) const
133{
134 Q_ASSERT_X(message != nullptr, "QGrpcOperation::read",
135 "Can't read to nullptr QProtobufMessage");
136 Q_D(const QGrpcOperation);
137 const auto ser = d->operationContext->serializer();
138 Q_ASSERT_X(ser, "QGrpcOperation", "The serializer is null");
139 if (!ser->deserialize(message, data: d->data)) {
140 qGrpcWarning() << "Unable to deserialize message(" << qToUnderlying(e: ser->lastError()) <<"): "
141 << ser->lastErrorString();
142 return false;
143 }
144 return true;
145}
146
147/*!
148 Tries to cancel the RPC immediately. Successful cancellation cannot be
149 guaranteed. Emits the \l finished signal with a \l {QtGrpc::StatusCode::}
150 {Cancelled} status code.
151
152 \sa QGrpcOperationContext::cancelRequested
153*/
154void QGrpcOperation::cancel()
155{
156 if (!isFinished()) {
157 Q_D(QGrpcOperation);
158 d->isFinished.storeRelaxed(newValue: true);
159 emit d->operationContext->cancelRequested();
160 Q_EMIT finished(status: QGrpcStatus{ QtGrpc::StatusCode::Cancelled,
161 tr(s: "Operation is cancelled by client") });
162 }
163}
164
165/*!
166 Returns the server metadata received from the channel.
167
168 \note For \l {QGrpcHttp2Channel} {HTTP/2 channels} it usually includes the
169 HTTP headers received from the server.
170*/
171const QHash<QByteArray, QByteArray> &QGrpcOperation::metadata() const & noexcept
172{
173 Q_D(const QGrpcOperation);
174 return d->operationContext->serverMetadata();
175}
176
177/*!
178 Returns the method name associated with this RPC operation.
179*/
180QLatin1StringView QGrpcOperation::method() const noexcept
181{
182 Q_D(const QGrpcOperation);
183 return d->operationContext->method();
184}
185
186/*!
187 Returns true if this operation has finished, meaning that no more
188 operations can happen on the corresponding RPC, otherwise returns false.
189*/
190bool QGrpcOperation::isFinished() const noexcept
191{
192 Q_D(const QGrpcOperation);
193 return d->isFinished.loadRelaxed();
194}
195
196/*!
197 \internal
198 \fn const QGrpcOperationContext &QGrpcOperation::context() const &
199 \fn QGrpcOperationContext &QGrpcOperation::context() &
200
201 Returns a reference to the internal operation context.
202*/
203const QGrpcOperationContext &QGrpcOperation::context() const & noexcept
204{
205 Q_D(const QGrpcOperation);
206 return *d->operationContext;
207}
208
209bool QGrpcOperation::event(QEvent *event)
210{
211 return QObject::event(event);
212}
213
214QT_END_NAMESPACE
215
216#include "moc_qgrpcoperation.cpp"
217

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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