Commit c012626e authored by Vitaly Fanaskov's avatar Vitaly Fanaskov

Extract functionality for submitting collected data

1) Added "interface" AbstractDataSubmitter which is the base class for
all data sending strategies.
2) Added "interface" AbstractJsonDataSubmitter which is the base class
for all JSON sending strategies.
3) Existing data sending functionality is extracted to the
DefaultDataSubmitter class.
4) Simplified existing solution: used built-in redirects resolving
instead of manually implemented.
5) Qt 5.9 is required.
parent e281901b
......@@ -19,6 +19,9 @@ set(userfeedback_core_srcs
surveyinfo.cpp
usagetimesource.cpp
auditloguicontroller.cpp
abstractjsondatasubmitter.cpp
abstractdatasubmitter.cpp
defaultdatasubmitter.cpp
${userfeedback_core_QM_LOADER}
)
......@@ -56,6 +59,9 @@ ecm_generate_headers(KUserFeedbackCore_HEADERS
SurveyInfo
UsageTimeSource
FeedbackConfigUiController
AbstractDataSubmitter
AbstractJsonDataSubmitter
DefaultDataSubmitter
REQUIRED_HEADERS KUserFeedbackCore_HEADERS
)
......
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 8/4/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#include "abstractdatasubmitter.h"
#include "abstractdatasubmitter_p.h"
namespace KUserFeedback {
AbstractDataSubmitterPrivate::AbstractDataSubmitterPrivate(QUrl serverUrl)
: serverUrl(std::move(serverUrl))
{
}
AbstractDataSubmitter::~AbstractDataSubmitter() = default;
QUrl AbstractDataSubmitter::serverUrl() const
{
return d_func()->serverUrl;
}
void AbstractDataSubmitter::setServerUrl(QUrl url)
{
d_func()->serverUrl = std::move(url);
}
AbstractDataSubmitter::AbstractDataSubmitter(QUrl url)
: d(new AbstractDataSubmitterPrivate(std::move(url)))
{
}
QString AbstractDataSubmitter::productId() const
{
return d_func()->productId;
}
void AbstractDataSubmitter::setProductId(QString id)
{
d_func()->productId = std::move(id);
}
} // namespace KUserFeedback
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 8/4/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef KUSERFEEDBACK_ABSTRACTDATASUBMITTER_H
#define KUSERFEEDBACK_ABSTRACTDATASUBMITTER_H
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include "kuserfeedbackcore_export.h"
namespace KUserFeedback {
class AbstractDataSubmitterPrivate;
//! The structure for storing the result of data sending operation.
struct KUSERFEEDBACKCORE_EXPORT DataSubmissionResult
{
bool ok; ///< Indicates whether the data submission succeeded or failed.
QString errorString; ///< Helds error description. Might be an empty.
QByteArray data; ///< Reply data.
};
//! AbstractDataSubmitter is the base class for all data sending strategies.
class KUSERFEEDBACKCORE_EXPORT AbstractDataSubmitter : public QObject
{
Q_OBJECT
public:
//! Destroys the object.
~AbstractDataSubmitter() override;
/*! Sends @p data to a server.
*
* This function must return immediately (try keeping this behavior, please).
* The @p dataSubmissionFinished signal will be sent when data transfer
* is finished or if an error is occurred during the data sending operation.
*
* @param data data to be sent.
*/
virtual void submit(const QByteArray &data) const = 0;
//! Returns base server URL.
QUrl serverUrl() const;
//! Returns unique product id. Might be either human-readable or not.
QString productId() const;
Q_SIGNALS:
/*! Emitted when data submission is finished.
*
* @param result holds operation result.
*/
void dataSubmissionFinished(const DataSubmissionResult &result) const;
public Q_SLOTS:
//! Sets new @p id.
void setProductId(QString id);
/*! Updates base server URL.
*
* @note Please, make sure that this is base server URL.
* You can extend it later, in the following methods:
* @p probeRequest() and @p dataRequest().
*/
void setServerUrl(QUrl url);
protected:
//! Constructs the object with optional @p url.
explicit AbstractDataSubmitter(QUrl url = QUrl());
private:
QScopedPointer<AbstractDataSubmitterPrivate> d;
Q_DECLARE_PRIVATE_D(d, AbstractDataSubmitter)
};
} // namespace KUserFeedback
#endif // KUSERFEEDBACK_ABSTRACTDATASUBMITTER_H
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 8/4/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef KUSERFEEDBACK_ABSTRACTDATASUBMITTERPRIVATE_H
#define KUSERFEEDBACK_ABSTRACTDATASUBMITTERPRIVATE_H
#include <QtCore/QUrl>
namespace KUserFeedback {
class AbstractDataSubmitterPrivate
{
public:
AbstractDataSubmitterPrivate(QUrl serverUrl = QUrl());
QUrl serverUrl;
QString productId;
};
} // namespace KUserFeedback
#endif // KUSERFEEDBACK_ABSTRACTDATASUBMITTERPRIVATE_H
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 21/3/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#include "abstractjsondatasubmitter.h"
#include "abstractjsondatasubmitter_p.h"
namespace KUserFeedback {
AbstractJsonDataSubmitterPrivate::AbstractJsonDataSubmitterPrivate(AbstractJsonDataSubmitter *interface)
: q_ptr(interface)
, networkAccessManager(new QNetworkAccessManager)
{
}
void AbstractJsonDataSubmitterPrivate::submit(const QByteArray &data) const
{
submitProbe(data);
}
static DataSubmissionResult toResult(QNetworkReply &reply)
{
bool ok = reply.error() == QNetworkReply::NoError;
return {ok, ok ? QString() : reply.errorString(), reply.readAll()};
}
void AbstractJsonDataSubmitterPrivate::submitData(const QByteArray &data) const
{
auto request = q_func()->dataRequest();
request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
request.setHeader(QNetworkRequest::ContentLengthHeader, data.length());
auto reply = networkAccessManager->post(request, data);
QObject::connect(reply, &QNetworkReply::finished,
[this, reply]
{
emit q_func()->dataSubmissionFinished(toResult(*reply));
reply->deleteLater();
});
}
void AbstractJsonDataSubmitterPrivate::submitProbe(const QByteArray &data) const
{
auto request = q_func()->probeRequest();
if (request.url().isValid()) {
auto reply = networkAccessManager->get(request);
QObject::connect(reply, &QNetworkReply::finished,
[this, reply, data]
{
if (reply->error() == QNetworkReply::NoError) {
submitData(data);
} else {
emit q_func()->dataSubmissionFinished(toResult(*reply));
}
reply->deleteLater();
});
} else {
submitData(data);
}
}
void AbstractJsonDataSubmitter::submit(const QByteArray &data) const
{
d_func()->submit(data);
}
QNetworkRequest AbstractJsonDataSubmitter::probeRequest() const
{
return QNetworkRequest();
}
AbstractJsonDataSubmitter::AbstractJsonDataSubmitter(QUrl url)
: AbstractDataSubmitter(std::move(url))
, d(new AbstractJsonDataSubmitterPrivate(this))
{
}
void AbstractJsonDataSubmitter::setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
{
Q_ASSERT(d_func()->networkAccessManager);
d_func()->networkAccessManager->setRedirectPolicy(policy);
}
AbstractJsonDataSubmitter::~AbstractJsonDataSubmitter() = default;
} // namespace KUserFeedback
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 21/3/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef KUSERFEEDBACK_ABSTRACTJSONDATASUBMITTER_H
#define KUSERFEEDBACK_ABSTRACTJSONDATASUBMITTER_H
#include <QtNetwork/QNetworkReply>
#include "abstractdatasubmitter.h"
QT_BEGIN_NAMESPACE
class QNetworkRequest;
QT_END_NAMESPACE
namespace KUserFeedback {
class AbstractJsonDataSubmitterPrivate;
//! Class for sending JSON data using QNetwork utilities.
class KUSERFEEDBACKCORE_EXPORT AbstractJsonDataSubmitter : public AbstractDataSubmitter
{
Q_OBJECT
public:
//! Destroys the object.
~AbstractJsonDataSubmitter() override;
//! See AbstractDataSubmitter::submit for further description.
void submit(const QByteArray &data) const override;
/*! Returns a request to be sent as a "probe".
*
* Default implementation returns an empty QNetworkRequest.
* This request is ignored if server url in returned request
* is not valid.
*
* @return "probe" request.
*/
virtual QNetworkRequest probeRequest() const;
/*! Returns a request to send a data.
*
* @note Content type header will be set to JSON before sending.
* Content length header will be automatically set before
* sending, based on data size, passed to the @p submit.
*
* @return a request will be used for data sending.
*/
virtual QNetworkRequest dataRequest() const = 0;
protected:
//! Constructs the object with optional @p url.
explicit AbstractJsonDataSubmitter(QUrl url = QUrl());
/*! Determines how redirects will be handled. The default value is: ManualRedirectPolicy.
*
* @param policy is a new policy to be applied.
*
* @note this value is automatically applied to all requests.
* If you want to specify different policy for either @p probeRequest or
* @p dataRequest, use the following method:
* QNetworkRequest::setAttribute(QNetworkRequest::RedirectPolicyAttribute, ...)
*/
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy);
private:
QScopedPointer<AbstractJsonDataSubmitterPrivate> d;
Q_DECLARE_PRIVATE_D(d, AbstractJsonDataSubmitter)
};
} // namespace KUserFeedback
Q_DECLARE_METATYPE(KUserFeedback::DataSubmissionResult)
#endif // KUSERFEEDBACK_ABSTRACTJSONDATASUBMITTER_H
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 21/3/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef KUSERFEEDBACK_ABSTRACTJSONDATASUBMITTERPRIVATE_H
#define KUSERFEEDBACK_ABSTRACTJSONDATASUBMITTERPRIVATE_H
#include <memory>
#include <QtNetwork/QNetworkAccessManager>
#include "abstractjsondatasubmitter.h"
namespace KUserFeedback {
class AbstractJsonDataSubmitterPrivate
{
public:
AbstractJsonDataSubmitterPrivate(AbstractJsonDataSubmitter *interface);
void submit(const QByteArray &data) const;
void submitData(const QByteArray &data) const;
void submitProbe(const QByteArray &data) const;
AbstractJsonDataSubmitter *q_ptr;
Q_DECLARE_PUBLIC(AbstractJsonDataSubmitter)
std::unique_ptr<QNetworkAccessManager> networkAccessManager;
};
} // namespace KUserFeedback
#endif // KUSERFEEDBACK_ABSTRACTJSONDATASUBMITTERPRIVATE_H
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 22/3/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#include "defaultdatasubmitter.h"
#include <QtCore/QRegularExpression>
#include "kuserfeedback_version.h"
namespace KUserFeedback {
DefaultDataSubmitter::DefaultDataSubmitter()
{
setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
}
static QUrl makeFullUrl(QUrl url, const QString &productId)
{
static const auto submitPath = QStringLiteral("/receiver/submit/");
auto path = url.path();
path.remove(QRegularExpression(QStringLiteral("/*$")));
path.append(submitPath).append(productId);
url.setPath(path);
return url;
}
static QString appVersionString()
{
return QStringLiteral("KUserFeedback/") + QStringLiteral(KUSERFEEDBACK_VERSION_STRING);
}
static constexpr int defaultRedirectsCount() { return 20; }
static QNetworkRequest requestWithAppVersion(QUrl url, const QString &productId)
{
QNetworkRequest request(makeFullUrl(std::move(url), productId));
request.setHeader(QNetworkRequest::UserAgentHeader, appVersionString());
request.setMaximumRedirectsAllowed(defaultRedirectsCount());
return request;
}
QNetworkRequest DefaultDataSubmitter::probeRequest() const
{
return requestWithAppVersion(serverUrl(), productId());
}
QNetworkRequest DefaultDataSubmitter::dataRequest() const
{
return requestWithAppVersion(serverUrl(), productId());
}
} // namespace KUserFeedback
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company
**
** Created 22/3/2019.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef KUSERFEEDBACK_DEFAULTDATASUBMITTER_H
#define KUSERFEEDBACK_DEFAULTDATASUBMITTER_H
#include "abstractjsondatasubmitter.h"
namespace KUserFeedback {
/*! Default submitter configured to send a probe first.