forked from qt-creator/qt-creator
Axivion: Analyze Dashboard server response for errors
Change-Id: Id66c3ad5d8a6c7d73e7ad781893c936b0829cfbf Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -14,4 +14,5 @@ add_qtc_plugin(Axivion
|
|||||||
dashboard/dto.cpp dashboard/dto.h
|
dashboard/dto.cpp dashboard/dto.h
|
||||||
dashboard/concat.cpp dashboard/concat.h
|
dashboard/concat.cpp dashboard/concat.h
|
||||||
dashboard/dashboardclient.cpp dashboard/dashboardclient.h
|
dashboard/dashboardclient.cpp dashboard/dashboardclient.h
|
||||||
|
dashboard/error.h dashboard/error.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,14 +26,12 @@ QtcPlugin {
|
|||||||
"axivionsettings.cpp",
|
"axivionsettings.cpp",
|
||||||
"axivionsettings.h",
|
"axivionsettings.h",
|
||||||
"axiviontr.h",
|
"axiviontr.h",
|
||||||
"dashboard/dashboardclient.cpp",
|
|
||||||
"dashboard/dashboardclient.h",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
cpp.includePaths: base.concat(["."]) // needed for the generated stuff below
|
cpp.includePaths: base.concat(["."]) // needed for the generated stuff below
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
name: "Generated DTOs"
|
name: "Dashboard Communication"
|
||||||
prefix: "dashboard/"
|
prefix: "dashboard/"
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
@@ -41,6 +39,10 @@ QtcPlugin {
|
|||||||
"concat.h",
|
"concat.h",
|
||||||
"dto.cpp",
|
"dto.cpp",
|
||||||
"dto.h",
|
"dto.h",
|
||||||
|
"dashboardclient.cpp",
|
||||||
|
"dashboardclient.h",
|
||||||
|
"error.cpp",
|
||||||
|
"error.h",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ void AxivionPluginPrivate::handleProjectInfo(DashboardClient::RawProjectInfo raw
|
|||||||
{
|
{
|
||||||
m_runningQuery = false;
|
m_runningQuery = false;
|
||||||
if (!rawInfo) {
|
if (!rawInfo) {
|
||||||
Core::MessageManager::writeFlashing(QStringLiteral(u"Axivion: ") + rawInfo.error());
|
Core::MessageManager::writeFlashing(
|
||||||
|
QStringLiteral(u"Axivion: %1").arg(rawInfo.error().message()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_currentProjectInfo = std::make_shared<const DashboardClient::ProjectInfo>(std::move(rawInfo.value()));
|
m_currentProjectInfo = std::make_shared<const DashboardClient::ProjectInfo>(std::move(rawInfo.value()));
|
||||||
|
|||||||
@@ -30,13 +30,17 @@ static void deleteLater(QObject *obj)
|
|||||||
obj->deleteLater();
|
obj->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
using RawBody = Utils::expected_str<DataWithOrigin<QByteArray>>;
|
using RawBody = Utils::expected<DataWithOrigin<QByteArray>, Error>;
|
||||||
|
|
||||||
|
static constexpr int httpStatusCodeOk = 200;
|
||||||
|
static constexpr QLatin1String jsonContentType{ "application/json" };
|
||||||
|
|
||||||
class RawBodyReader final
|
class RawBodyReader final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RawBodyReader(std::shared_ptr<QNetworkReply> reply)
|
RawBodyReader(std::shared_ptr<QNetworkReply> reply, QAnyStringView expectedContentType)
|
||||||
: m_reply(std::move(reply))
|
: m_reply(std::move(reply)),
|
||||||
|
m_expectedContentType(expectedContentType)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
~RawBodyReader() { }
|
~RawBodyReader() { }
|
||||||
@@ -44,19 +48,47 @@ public:
|
|||||||
RawBody operator()()
|
RawBody operator()()
|
||||||
{
|
{
|
||||||
QNetworkReply::NetworkError error = m_reply->error();
|
QNetworkReply::NetworkError error = m_reply->error();
|
||||||
if (error != QNetworkReply::NetworkError::NoError)
|
int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
return tl::make_unexpected(QString::number(error)
|
QString contentType = m_reply->header(QNetworkRequest::ContentTypeHeader)
|
||||||
+ QLatin1String(": ")
|
.toString()
|
||||||
+ m_reply->errorString());
|
.split(';')
|
||||||
|
.constFirst()
|
||||||
|
.trimmed()
|
||||||
|
.toLower();
|
||||||
|
if (error == QNetworkReply::NetworkError::NoError
|
||||||
|
&& statusCode == httpStatusCodeOk
|
||||||
|
&& contentType == m_expectedContentType) {
|
||||||
return DataWithOrigin(m_reply->url(), m_reply->readAll());
|
return DataWithOrigin(m_reply->url(), m_reply->readAll());
|
||||||
}
|
}
|
||||||
|
if (contentType == jsonContentType) {
|
||||||
|
try {
|
||||||
|
return tl::make_unexpected(DashboardError(
|
||||||
|
m_reply->url(),
|
||||||
|
statusCode,
|
||||||
|
m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(),
|
||||||
|
Dto::ErrorDto::deserialize(m_reply->readAll())));
|
||||||
|
} catch (const Dto::invalid_dto_exception &) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (statusCode != 0) {
|
||||||
|
return tl::make_unexpected(HttpError(
|
||||||
|
m_reply->url(),
|
||||||
|
statusCode,
|
||||||
|
m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(),
|
||||||
|
QString::fromUtf8(m_reply->readAll()))); // encoding?
|
||||||
|
}
|
||||||
|
return tl::make_unexpected(
|
||||||
|
NetworkError(m_reply->url(), error, m_reply->errorString()));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<QNetworkReply> m_reply;
|
std::shared_ptr<QNetworkReply> m_reply;
|
||||||
|
QAnyStringView m_expectedContentType;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static Utils::expected_str<DataWithOrigin<T>> RawBodyParser(RawBody rawBody)
|
static Utils::expected<DataWithOrigin<T>, Error> RawBodyParser(RawBody rawBody)
|
||||||
{
|
{
|
||||||
if (!rawBody)
|
if (!rawBody)
|
||||||
return tl::make_unexpected(std::move(rawBody.error()));
|
return tl::make_unexpected(std::move(rawBody.error()));
|
||||||
@@ -65,7 +97,8 @@ static Utils::expected_str<DataWithOrigin<T>> RawBodyParser(RawBody rawBody)
|
|||||||
return DataWithOrigin(std::move(rawBody.value().origin),
|
return DataWithOrigin(std::move(rawBody.value().origin),
|
||||||
std::move(data));
|
std::move(data));
|
||||||
} catch (const Dto::invalid_dto_exception &e) {
|
} catch (const Dto::invalid_dto_exception &e) {
|
||||||
return tl::make_unexpected(QString::fromUtf8(e.what()));
|
return tl::make_unexpected(GeneralError(std::move(rawBody.value().origin),
|
||||||
|
QString::fromUtf8(e.what())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +124,7 @@ QFuture<DashboardClient::RawProjectInfo> DashboardClient::fetchProjectInfo(const
|
|||||||
std::shared_ptr<QNetworkReply> reply{ this->m_networkAccessManager.get(request), deleteLater };
|
std::shared_ptr<QNetworkReply> reply{ this->m_networkAccessManager.get(request), deleteLater };
|
||||||
return QtFuture::connect(reply.get(), &QNetworkReply::finished)
|
return QtFuture::connect(reply.get(), &QNetworkReply::finished)
|
||||||
.onCanceled(reply.get(), [reply] { reply->abort(); })
|
.onCanceled(reply.get(), [reply] { reply->abort(); })
|
||||||
.then(RawBodyReader(reply))
|
.then(RawBodyReader(reply, jsonContentType))
|
||||||
.then(QtFuture::Launch::Async, &RawBodyParser<Dto::ProjectInfoDto>);
|
.then(QtFuture::Launch::Async, &RawBodyParser<Dto::ProjectInfoDto>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dashboard/dto.h"
|
#include "dashboard/dto.h"
|
||||||
|
#include "dashboard/error.h"
|
||||||
|
|
||||||
#include <utils/expected.h>
|
#include <utils/expected.h>
|
||||||
#include <utils/networkaccessmanager.h>
|
#include <utils/networkaccessmanager.h>
|
||||||
@@ -32,7 +33,7 @@ class DashboardClient
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ProjectInfo = DataWithOrigin<Dto::ProjectInfoDto>;
|
using ProjectInfo = DataWithOrigin<Dto::ProjectInfoDto>;
|
||||||
using RawProjectInfo = Utils::expected_str<ProjectInfo>;
|
using RawProjectInfo = Utils::expected<ProjectInfo, Error>;
|
||||||
|
|
||||||
DashboardClient(Utils::NetworkAccessManager &networkAccessManager);
|
DashboardClient(Utils::NetworkAccessManager &networkAccessManager);
|
||||||
|
|
||||||
|
|||||||
107
src/plugins/axivion/dashboard/error.cpp
Normal file
107
src/plugins/axivion/dashboard/error.cpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022-current by Axivion GmbH
|
||||||
|
* https://www.axivion.com/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dashboard/error.h"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace Axivion::Internal
|
||||||
|
{
|
||||||
|
|
||||||
|
CommunicationError::CommunicationError(QUrl replyUrl)
|
||||||
|
: replyUrl(std::move(replyUrl))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneralError::GeneralError(QUrl replyUrl, QString message)
|
||||||
|
: CommunicationError(replyUrl),
|
||||||
|
message(std::move(message))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkError::NetworkError(QUrl replyUrl,
|
||||||
|
QNetworkReply::NetworkError networkError,
|
||||||
|
QString networkErrorString)
|
||||||
|
: CommunicationError(std::move(replyUrl)),
|
||||||
|
networkError(std::move(networkError)),
|
||||||
|
networkErrorString(std::move(networkErrorString))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpError::HttpError(QUrl replyUrl, int httpStatusCode, QString httpReasonPhrase, QString body)
|
||||||
|
: CommunicationError(std::move(replyUrl)),
|
||||||
|
httpStatusCode(httpStatusCode),
|
||||||
|
httpReasonPhrase(std::move(httpReasonPhrase)),
|
||||||
|
body(std::move(body))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DashboardError::DashboardError(QUrl replyUrl, int httpStatusCode, QString httpReasonPhrase, Dto::ErrorDto error)
|
||||||
|
: CommunicationError(std::move(replyUrl)),
|
||||||
|
httpStatusCode(httpStatusCode),
|
||||||
|
httpReasonPhrase(std::move(httpReasonPhrase)),
|
||||||
|
dashboardVersion(std::move(error.dashboardVersionNumber)),
|
||||||
|
type(std::move(error.type)),
|
||||||
|
message(std::move(error.message))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error(GeneralError error) : m_error(std::move(error))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error(NetworkError error) : m_error(std::move(error))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error(HttpError error) : m_error(std::move(error))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error(DashboardError error) : m_error(std::move(error))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.cppstories.com/2018/09/visit-variants/
|
||||||
|
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||||
|
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; // line not needed in C++20...
|
||||||
|
|
||||||
|
QString Error::message() const
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
overloaded{
|
||||||
|
[](const GeneralError &error) {
|
||||||
|
return QStringLiteral(u"GeneralError (%1) %2")
|
||||||
|
.arg(error.replyUrl.toString(),
|
||||||
|
error.message);
|
||||||
|
},
|
||||||
|
[](const NetworkError &error) {
|
||||||
|
return QStringLiteral(u"NetworkError (%1) %2: %3")
|
||||||
|
.arg(error.replyUrl.toString(),
|
||||||
|
QString::number(error.networkError),
|
||||||
|
error.networkErrorString);
|
||||||
|
},
|
||||||
|
[](const HttpError &error) {
|
||||||
|
return QStringLiteral(u"HttpError (%1) %2: %3\n%4")
|
||||||
|
.arg(error.replyUrl.toString(),
|
||||||
|
QString::number(error.httpStatusCode),
|
||||||
|
error.httpReasonPhrase,
|
||||||
|
error.body);
|
||||||
|
},
|
||||||
|
[](const DashboardError &error) {
|
||||||
|
return QStringLiteral(u"DashboardError (%1) [%2 %3] %4: %5")
|
||||||
|
.arg(error.replyUrl.toString(),
|
||||||
|
QString::number(error.httpStatusCode),
|
||||||
|
error.httpReasonPhrase,
|
||||||
|
error.type,
|
||||||
|
error.message);
|
||||||
|
},
|
||||||
|
}, this->m_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Axivion::Internal
|
||||||
86
src/plugins/axivion/dashboard/error.h
Normal file
86
src/plugins/axivion/dashboard/error.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-current by Axivion GmbH
|
||||||
|
* https://www.axivion.com/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dashboard/dto.h"
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace Axivion::Internal
|
||||||
|
{
|
||||||
|
|
||||||
|
class CommunicationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QUrl replyUrl;
|
||||||
|
|
||||||
|
CommunicationError(QUrl replyUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
class GeneralError : public CommunicationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString message;
|
||||||
|
|
||||||
|
GeneralError(QUrl replyUrl, QString message);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkError : public CommunicationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QNetworkReply::NetworkError networkError;
|
||||||
|
QString networkErrorString;
|
||||||
|
|
||||||
|
NetworkError(QUrl replyUrl, QNetworkReply::NetworkError networkError, QString networkErrorString);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpError : public CommunicationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int httpStatusCode;
|
||||||
|
QString httpReasonPhrase;
|
||||||
|
QString body;
|
||||||
|
|
||||||
|
HttpError(QUrl replyUrl, int httpStatusCode, QString httpReasonPhrase, QString body);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DashboardError : public CommunicationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int httpStatusCode;
|
||||||
|
QString httpReasonPhrase;
|
||||||
|
std::optional<QString> dashboardVersion;
|
||||||
|
QString type;
|
||||||
|
QString message;
|
||||||
|
|
||||||
|
DashboardError(QUrl replyUrl, int httpStatusCode, QString httpReasonPhrase, Dto::ErrorDto error);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Error(GeneralError error);
|
||||||
|
|
||||||
|
Error(NetworkError error);
|
||||||
|
|
||||||
|
Error(HttpError error);
|
||||||
|
|
||||||
|
Error(DashboardError error);
|
||||||
|
|
||||||
|
QString message() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::variant<GeneralError,
|
||||||
|
NetworkError,
|
||||||
|
HttpError,
|
||||||
|
DashboardError> m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Axivion::Internal
|
||||||
Reference in New Issue
Block a user