Axivion: Make initial dashboard fetch a part of authorization

Ensure that after authorization we have fetched the initial
dashboard info. If the fetch fails, we remove the stored
ApiToken from the key chain.

Change-Id: I894b9d8bc8ade8ec6fa29391fafa67ad5b4fabba
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Jarek Kobus
2024-02-28 01:17:41 +01:00
parent 64a77d8c83
commit bcdcaab7b7

View File

@@ -128,8 +128,27 @@ static QString credentialKey()
return escape(settings().server.username) + '@' + escape(settings().server.dashboard);
}
static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInfoDto &infoDto)
template <typename DtoType>
struct GetDtoStorage
{
QUrl url;
std::optional<QByteArray> credential;
std::optional<DtoType> dtoData;
};
template <typename DtoType>
struct PostDtoStorage
{
QUrl url;
std::optional<QByteArray> credential;
QByteArray csrfToken;
QByteArray writeData;
std::optional<DtoType> dtoData;
};
static DashboardInfo toDashboardInfo(const GetDtoStorage<Dto::DashboardInfoDto> &dashboardStorage)
{
const Dto::DashboardInfoDto &infoDto = *dashboardStorage.dtoData;
const QVersionNumber versionNumber = infoDto.dashboardVersionNumber
? QVersionNumber::fromString(*infoDto.dashboardVersionNumber) : QVersionNumber();
@@ -142,7 +161,7 @@ static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInf
projectUrls.insert(project.name, project.url);
}
}
return {source, versionNumber, projects, projectUrls, infoDto.checkCredentialsUrl};
return {dashboardStorage.url, versionNumber, projects, projectUrls, infoDto.checkCredentialsUrl};
}
QString IssueListSearch::toQuery() const
@@ -376,24 +395,6 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QBy
return {NetworkQueryTask(onQuerySetup, onQueryDone)};
}
template <typename DtoType>
struct GetDtoStorage
{
QUrl url;
std::optional<QByteArray> credential;
std::optional<DtoType> dtoData;
};
template <typename DtoType>
struct PostDtoStorage
{
QUrl url;
std::optional<QByteArray> credential;
QByteArray csrfToken;
QByteArray writeData;
std::optional<DtoType> dtoData;
};
template <typename DtoType, template <typename> typename DtoStorageType>
static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
{
@@ -419,7 +420,8 @@ static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
query.setNetworkAccessManager(&dd->m_networkAccessManager);
};
const auto onNetworkQueryDone = [storage](const NetworkQuery &query, DoneWith doneWith) {
const auto onNetworkQueryDone = [storage, dtoStorage](const NetworkQuery &query,
DoneWith doneWith) {
QNetworkReply *reply = query.reply();
const QNetworkReply::NetworkError error = reply->error();
const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@@ -432,6 +434,7 @@ static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk
&& contentType == s_jsonContentType) {
*storage = reply->readAll();
dtoStorage->url = reply->url();
return DoneResult::Success;
}
@@ -521,8 +524,7 @@ static Group authorizationRecipe()
const auto onUnauthorizedGroupDone = [unauthorizedDashboardStorage] {
if (unauthorizedDashboardStorage->dtoData) {
dd->m_serverAccess = ServerAccess::NoAuthorization;
dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard,
*unauthorizedDashboardStorage->dtoData);
dd->m_dashboardInfo = toDashboardInfo(*unauthorizedDashboardStorage);
} else {
dd->m_serverAccess = ServerAccess::WithAuthorization;
}
@@ -549,7 +551,7 @@ static Group authorizationRecipe()
const Storage<QString> passwordStorage;
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> dashboardStorage;
const auto onDashboardGroupSetup = [passwordStorage, dashboardStorage] {
const auto onPasswordGroupSetup = [passwordStorage, dashboardStorage] {
if (dd->m_apiToken)
return SetupResult::StopWithSuccess;
@@ -572,8 +574,7 @@ static Group authorizationRecipe()
if (!dashboardStorage->dtoData)
return SetupResult::StopWithSuccess;
dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard,
*dashboardStorage->dtoData);
dd->m_dashboardInfo = toDashboardInfo(*dashboardStorage);
const Dto::DashboardInfoDto &dashboardDto = *dashboardStorage->dtoData;
if (!dashboardDto.userApiTokenUrl)
@@ -607,6 +608,29 @@ static Group authorizationRecipe()
return DoneResult::Success;
};
const auto onDashboardGroupSetup = [dashboardStorage] {
if (dd->m_dashboardInfo || dd->m_serverAccess != ServerAccess::WithAuthorization
|| !dd->m_apiToken) {
return SetupResult::StopWithSuccess; // Unauthorized access should have collect dashboard before
}
dashboardStorage->credential = "AxToken " + *dd->m_apiToken;
dashboardStorage->url = QUrl(settings().server.dashboard);
return SetupResult::Continue;
};
const auto onDeleteCredentialSetup = [dashboardStorage](CredentialQuery &credential) {
if (dashboardStorage->dtoData) {
dd->m_dashboardInfo = toDashboardInfo(*dashboardStorage);
return SetupResult::StopWithSuccess;
}
dd->m_apiToken = {};
MessageManager::writeFlashing(QString("Axivion: %1")
.arg(Tr::tr("The stored ApiToken is not valid anymore, removing it.")));
credential.setOperation(CredentialOperation::Delete);
credential.setService(s_axivionKeychainService);
credential.setKey(credentialKey());
return SetupResult::Continue;
};
return {
Group {
unauthorizedDashboardStorage,
@@ -620,7 +644,7 @@ static Group authorizationRecipe()
Group {
passwordStorage,
dashboardStorage,
onGroupSetup(onDashboardGroupSetup),
onGroupSetup(onPasswordGroupSetup),
Group { // GET DashboardInfoDto
finishAllAndSuccess,
dtoRecipe(dashboardStorage)
@@ -631,6 +655,13 @@ static Group authorizationRecipe()
dtoRecipe(apiTokenStorage),
CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error)
}
},
Group {
finishAllAndSuccess,
dashboardStorage,
onGroupSetup(onDashboardGroupSetup),
dtoRecipe(dashboardStorage),
CredentialQueryTask(onDeleteCredentialSetup)
}
}
};
@@ -671,27 +702,22 @@ Group dashboardInfoRecipe(const DashboardInfoHandler &handler)
{
const auto onSetup = [handler] {
if (dd->m_dashboardInfo) {
if (handler)
handler(*dd->m_dashboardInfo);
handler(*dd->m_dashboardInfo);
return SetupResult::StopWithSuccess;
}
return SetupResult::Continue;
};
const auto onDone = [handler] {
if (handler)
handler(make_unexpected(QString("Error"))); // TODO: Collect error message in the storage.
};
const auto resultHandler = [handler](const Dto::DashboardInfoDto &data) {
dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, data);
if (handler)
const auto onDone = [handler](DoneWith result) {
if (result == DoneWith::Success && dd->m_dashboardInfo)
handler(*dd->m_dashboardInfo);
else
handler(make_unexpected(QString("Error"))); // TODO: Collect error message in the storage.
};
const Group root {
onGroupSetup(onSetup), // Stops if cache exists.
fetchDataRecipe<Dto::DashboardInfoDto>(settings().server.dashboard, resultHandler),
onGroupDone(onDone, CallDoneIf::Error)
authorizationRecipe(),
onGroupDone(onDone)
};
return root;
}
@@ -770,7 +796,7 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName)
};
const Group root {
dashboardInfoRecipe(),
authorizationRecipe(),
TaskTreeTask(onTaskTreeSetup)
};
m_taskTreeRunner.start(root);