summaryrefslogtreecommitdiffstats
path: root/src/grpc/qgrpcoperation.cpp
blob: 0da120d934b880e0bdf6f7659df5bf00cb78d6a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Copyright (C) 2022 The Qt Company Ltd.
// Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <QtCore/private/qobject_p.h>
#include <QtCore/qatomic.h>
#include <QtCore/qeventloop.h>
#include <QtCore/qpointer.h>
#include <QtGrpc/private/qtgrpcglobal_p.h>
#include <QtGrpc/qgrpcchanneloperation.h>
#include <QtGrpc/qgrpcoperation.h>

QT_BEGIN_NAMESPACE

using namespace Qt::StringLiterals;

/*!
    \class QGrpcOperation
    \inmodule QtGrpc
    \brief The QGrpcOperation class implements common logic to
           handle the gRPC communication from the client side.
*/

/*!
    \fn template <typename T> std::optional<T> QGrpcOperation::read() const

    Reads a message from a raw byte array stored within this QGrpcOperation
    instance.

    Returns an optional deserialized message. On failure, \c {std::nullopt} is
    returned.

    The error can be retrieved using \l deserializationError.

    \sa read, deserializationError, deserializationErrorString
*/

/*!
    \fn void QGrpcOperation::finished()

    This signal indicates the end of communication for this call.

    If this signal is emitted by the stream then this stream is successfully
    closed either by client or server.
*/

/*!
    \fn void QGrpcOperation::errorOccurred(const QGrpcStatus &status)

    This signal is emitted when an error with \a status occurs in the channel.

    \sa QAbstractGrpcClient::errorOccurred
*/

class QGrpcOperationPrivate : public QObjectPrivate
{
    Q_DECLARE_PUBLIC(QGrpcOperation)
public:
    QGrpcOperationPrivate(std::shared_ptr<QGrpcChannelOperation> _channelOperation)
        : channelOperation(std::move(_channelOperation))
    {
    }

    QByteArray data;
    std::shared_ptr<QGrpcChannelOperation> channelOperation;
    QAtomicInteger<bool> isFinished{ false };
};

QGrpcOperation::QGrpcOperation(std::shared_ptr<QGrpcChannelOperation> channelOperation,
                               QObject *parent)
    : QObject(*new QGrpcOperationPrivate(std::move(channelOperation)), parent)
{
    [[maybe_unused]] bool valid =
            QObject::connect(d_func()->channelOperation.get(), &QGrpcChannelOperation::dataReady,
                             this, [this](const QByteArray &data) {
                                 Q_D(QGrpcOperation);
                                 d->data = data;
                             });
    Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
               "Unable to make connection to the 'dataReady' signal");

    valid = QObject::connect(d_func()->channelOperation.get(),
                             &QGrpcChannelOperation::errorOccurred, this,
                             [this](const auto &status) {
                                 d_func()->isFinished.storeRelaxed(true);
                                 emit this->errorOccurred(status);
                             });
    Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
               "Unable to make connection to the 'errorOccurred' signal");

    valid = QObject::connect(d_func()->channelOperation.get(), &QGrpcChannelOperation::finished,
                             this, [this]() {
                                 d_func()->isFinished.storeRelaxed(true);
                                 emit this->finished();
                             });
    Q_ASSERT_X(valid, "QGrpcOperation::QGrpcOperation",
               "Unable to make connection to the 'finished' signal");
}

QGrpcOperation::~QGrpcOperation() = default;

/*!
    \internal
    Getter of the data received from the channel.
*/
QByteArray QGrpcOperation::data() const noexcept
{
    return d_func()->data;
}

/*!
    \since 6.8
    Reads a message from a raw byte array which is stored within this
    QGrpcOperation instance.

    The function writes the deserialized value to the \a message pointer.

    If the deserialization is successful, this function returns \c true.
    Otherwise, it returns \c false, and the error can be retrieved with \l
    deserializationError.

    \sa read, deserializationError, deserializationErrorString
*/
bool QGrpcOperation::read(QProtobufMessage *message) const
{
    Q_ASSERT_X(message != nullptr, "QGrpcOperation::read",
               "Can't read to nullptr QProtobufMessage");
    const auto ser = d_func()->channelOperation->serializer();
    return ser && ser->deserialize(message, data());
}

/*!
   \since 6.8

   Returns the last deserialization error.

   \sa QAbstractProtobufSerializer::deserializationError
*/
QAbstractProtobufSerializer::DeserializationError QGrpcOperation::deserializationError() const
{
    const auto ser = d_func()->channelOperation->serializer();
    if (!ser)
        return QAbstractProtobufSerializer::NoDeserializerError;
    return ser->deserializationError();
}

/*!
   \since 6.8

   Returns the last deserialization error string.

   \sa QAbstractProtobufSerializer::deserializationErrorString
*/
QString QGrpcOperation::deserializationErrorString() const
{
    const auto ser = d_func()->channelOperation->serializer();
    if (!ser)
        return QStringLiteral("serializer not available");
    return ser->deserializationErrorString();
}

/*!
    Getter of the metadata received from the channel. For the HTTP2 channels it
    usually contains the HTTP headers received from the server.
*/
const QGrpcMetadata &QGrpcOperation::metadata() const noexcept
{
    return d_func()->channelOperation->serverMetadata();
}

/*!
    Getter of the method that this operation was initialized with.
*/
QLatin1StringView QGrpcOperation::method() const noexcept
{
    return d_func()->channelOperation->method();
}

/*!
    \internal
    Returns a pointer to the assigned channel-side QGrpcChannelOperation.
*/
QGrpcChannelOperation *QGrpcOperation::channelOperation() const noexcept
{
    return d_func()->channelOperation.get();
}

/*!
    \internal
    Getter of the serializer that QGrpcOperation was constructed with.
*/
std::shared_ptr<const QAbstractProtobufSerializer> QGrpcOperation::serializer() const noexcept
{
    return d_func()->channelOperation->serializer();
}

/*!
    Attempts to cancel the operation in a channel and immediately emits
    \l{QGrpcOperation::errorOccurred} with the \l{QGrpcStatus::Cancelled}
    status code.

    Any manipulation of the operation after this call has no effect.
*/
void QGrpcOperation::cancel()
{
    d_func()->isFinished.storeRelaxed(true);
    emit d_func()->channelOperation->cancelled();
    emit errorOccurred(QGrpcStatus{ QGrpcStatus::Cancelled,
                                    tr("Operation is cancelled by client") });
}

/*!
    Returns true when QGrpcOperation finished its workflow,
    meaning it was finished, canceled, or error occurred, otherwise returns false.
*/
bool QGrpcOperation::isFinished() const noexcept
{
    return d_func()->isFinished.loadRelaxed();
}

QT_END_NAMESPACE

#include "moc_qgrpcoperation.cpp"