forked from qt-creator/qt-creator
Axivion: Fetch issues
Change-Id: Ib33c507c8036fe84291ac3ff3fb08eed46afb07c Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -8,6 +8,8 @@
|
|||||||
#include "dashboard/dto.h"
|
#include "dashboard/dto.h"
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/treemodel.h>
|
||||||
|
#include <utils/basetreeview.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
@@ -176,6 +178,8 @@ class IssuesWidget : public QScrollArea
|
|||||||
public:
|
public:
|
||||||
explicit IssuesWidget(QWidget *parent = nullptr);
|
explicit IssuesWidget(QWidget *parent = nullptr);
|
||||||
void updateUi();
|
void updateUi();
|
||||||
|
void setTableDto(const Dto::TableInfoDto &dto);
|
||||||
|
void addIssues(const Dto::IssueTableDto &dto);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateTableView();
|
void updateTableView();
|
||||||
@@ -190,6 +194,8 @@ private:
|
|||||||
QComboBox *m_versionStart = nullptr;
|
QComboBox *m_versionStart = nullptr;
|
||||||
QComboBox *m_versionEnd = nullptr;
|
QComboBox *m_versionEnd = nullptr;
|
||||||
QLineEdit *m_pathGlobFilter = nullptr; // FancyLineEdit instead?
|
QLineEdit *m_pathGlobFilter = nullptr; // FancyLineEdit instead?
|
||||||
|
Utils::BaseTreeView *m_issuesView = nullptr;
|
||||||
|
Utils::TreeModel<> *m_issuesModel = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
IssuesWidget::IssuesWidget(QWidget *parent)
|
IssuesWidget::IssuesWidget(QWidget *parent)
|
||||||
@@ -231,7 +237,13 @@ IssuesWidget::IssuesWidget(QWidget *parent)
|
|||||||
m_pathGlobFilter->setPlaceholderText(Tr::tr("Path globbing"));
|
m_pathGlobFilter->setPlaceholderText(Tr::tr("Path globbing"));
|
||||||
m_filtersLayout->addWidget(m_pathGlobFilter);
|
m_filtersLayout->addWidget(m_pathGlobFilter);
|
||||||
layout->addLayout(m_filtersLayout);
|
layout->addLayout(m_filtersLayout);
|
||||||
// TODO the issues table
|
m_issuesView = new Utils::BaseTreeView(this);
|
||||||
|
m_issuesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
m_issuesView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
m_issuesView->enableColumnHiding();
|
||||||
|
m_issuesModel = new Utils::TreeModel;
|
||||||
|
m_issuesView->setModel(m_issuesModel);
|
||||||
|
layout->addWidget(m_issuesView);
|
||||||
layout->addStretch(1);
|
layout->addStretch(1);
|
||||||
setWidget(widget);
|
setWidget(widget);
|
||||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
@@ -273,12 +285,75 @@ void IssuesWidget::updateUi()
|
|||||||
|
|
||||||
// TODO fill owners
|
// TODO fill owners
|
||||||
m_filtersLayout->setEnabled(true);
|
m_filtersLayout->setEnabled(true);
|
||||||
|
if (info.issueKinds.size())
|
||||||
|
m_currentPrefix = info.issueKinds.front().prefix;
|
||||||
|
updateTableView();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IssuesWidget::setTableDto(const Dto::TableInfoDto &dto)
|
||||||
|
{
|
||||||
|
m_currentTableInfo.emplace(dto);
|
||||||
|
|
||||||
|
// update issues table layout - for now just simple approach
|
||||||
|
Utils::TreeModel<> *issuesModel = new Utils::TreeModel;
|
||||||
|
QStringList columnHeaders;
|
||||||
|
QStringList hiddenColumns;
|
||||||
|
for (const Dto::ColumnInfoDto &column : dto.columns) {
|
||||||
|
columnHeaders << column.key;
|
||||||
|
if (!column.showByDefault)
|
||||||
|
hiddenColumns << column.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
issuesModel->setHeader(columnHeaders);
|
||||||
|
|
||||||
|
auto oldModel = m_issuesModel;
|
||||||
|
m_issuesModel = issuesModel;
|
||||||
|
m_issuesView->setModel(issuesModel);
|
||||||
|
delete oldModel;
|
||||||
|
int counter = 0;
|
||||||
|
for (const QString &header : std::as_const(columnHeaders))
|
||||||
|
m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header));
|
||||||
|
|
||||||
|
// first time lookup... should we cache and maybe represent old data?
|
||||||
|
IssueListSearch search;
|
||||||
|
search.kind = m_currentPrefix;
|
||||||
|
fetchIssues(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IssuesWidget::addIssues(const Dto::IssueTableDto &dto)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_currentTableInfo.has_value(), return);
|
||||||
|
// handle added/removed/total ?
|
||||||
|
|
||||||
|
const std::vector<Dto::ColumnInfoDto> tableColumns = m_currentTableInfo->columns;
|
||||||
|
// const std::vector<Dto::ColumnInfoDto> columns = dto.columns.value();
|
||||||
|
const std::vector<std::map<QString, Dto::Any>> rows = dto.rows;
|
||||||
|
for (auto row : rows) {
|
||||||
|
QStringList data;
|
||||||
|
for (auto column : tableColumns) {
|
||||||
|
auto it = row.find(column.key);
|
||||||
|
if (it != row.end()) {
|
||||||
|
if (it->second.isString())
|
||||||
|
data << it->second.getString();
|
||||||
|
else if (it->second.isDouble())
|
||||||
|
data << QString::number(it->second.getDouble());
|
||||||
|
else if (it->second.isBool())
|
||||||
|
data << QString("%1").arg(it->second.getBool());
|
||||||
|
else
|
||||||
|
data << "not yet";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Utils::StaticTreeItem *it = new Utils::StaticTreeItem(data);
|
||||||
|
m_issuesModel->rootItem()->appendChild(it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IssuesWidget::updateTableView()
|
void IssuesWidget::updateTableView()
|
||||||
{
|
{
|
||||||
// fetch table dto and apply
|
QTC_ASSERT(!m_currentPrefix.isEmpty(), return);
|
||||||
// on done fetch first data for the selected issues
|
// fetch table dto and apply, on done fetch first data for the selected issues
|
||||||
|
fetchIssueTableLayout(m_currentPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
AxivionOutputPane::AxivionOutputPane(QObject *parent)
|
AxivionOutputPane::AxivionOutputPane(QObject *parent)
|
||||||
@@ -387,6 +462,18 @@ void AxivionOutputPane::updateDashboard()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AxivionOutputPane::setTableDto(const Dto::TableInfoDto &dto)
|
||||||
|
{
|
||||||
|
if (auto issues = static_cast<IssuesWidget *>(m_outputWidget->widget(1)))
|
||||||
|
issues->setTableDto(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AxivionOutputPane::addIssues(const Dto::IssueTableDto &dto)
|
||||||
|
{
|
||||||
|
if (auto issues = static_cast<IssuesWidget *>(m_outputWidget->widget(1)))
|
||||||
|
issues->addIssues(dto);
|
||||||
|
}
|
||||||
|
|
||||||
void AxivionOutputPane::updateAndShowRule(const QString &ruleHtml)
|
void AxivionOutputPane::updateAndShowRule(const QString &ruleHtml)
|
||||||
{
|
{
|
||||||
if (auto browser = static_cast<QTextBrowser *>(m_outputWidget->widget(2))) {
|
if (auto browser = static_cast<QTextBrowser *>(m_outputWidget->widget(2))) {
|
||||||
|
@@ -11,6 +11,11 @@ QT_END_NAMESPACE
|
|||||||
|
|
||||||
namespace Axivion::Internal {
|
namespace Axivion::Internal {
|
||||||
|
|
||||||
|
namespace Dto {
|
||||||
|
class IssueTableDto;
|
||||||
|
class TableInfoDto;
|
||||||
|
}
|
||||||
|
|
||||||
class AxivionOutputPane : public Core::IOutputPane
|
class AxivionOutputPane : public Core::IOutputPane
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -33,6 +38,8 @@ public:
|
|||||||
|
|
||||||
void updateDashboard();
|
void updateDashboard();
|
||||||
void updateAndShowRule(const QString &ruleHtml);
|
void updateAndShowRule(const QString &ruleHtml);
|
||||||
|
void setTableDto(const Dto::TableInfoDto &dto);
|
||||||
|
void addIssues(const Dto::IssueTableDto &dto);
|
||||||
private:
|
private:
|
||||||
QStackedWidget *m_outputWidget = nullptr;
|
QStackedWidget *m_outputWidget = nullptr;
|
||||||
};
|
};
|
||||||
|
@@ -69,6 +69,16 @@ QIcon iconForIssue(const QString &prefix)
|
|||||||
return it.value();
|
return it.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString IssueListSearch::toQuery() const
|
||||||
|
{
|
||||||
|
if (kind.isEmpty())
|
||||||
|
return {};
|
||||||
|
QString result;
|
||||||
|
result.append(QString("?kind=%1&offset=%2&limit=%3").arg(kind).arg(offset).arg(limit));
|
||||||
|
// TODO other params
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
class AxivionPluginPrivate : public QObject
|
class AxivionPluginPrivate : public QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -76,6 +86,8 @@ public:
|
|||||||
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||||
void onStartupProjectChanged();
|
void onStartupProjectChanged();
|
||||||
void fetchProjectInfo(const QString &projectName);
|
void fetchProjectInfo(const QString &projectName);
|
||||||
|
void fetchIssueTableLayout(const QString &prefix);
|
||||||
|
void fetchIssues(const IssueListSearch &search);
|
||||||
void handleOpenedDocs(ProjectExplorer::Project *project);
|
void handleOpenedDocs(ProjectExplorer::Project *project);
|
||||||
void onDocumentOpened(Core::IDocument *doc);
|
void onDocumentOpened(Core::IDocument *doc);
|
||||||
void onDocumentClosed(Core::IDocument * doc);
|
void onDocumentClosed(Core::IDocument * doc);
|
||||||
@@ -127,6 +139,18 @@ void fetchProjectInfo(const QString &projectName)
|
|||||||
dd->fetchProjectInfo(projectName);
|
dd->fetchProjectInfo(projectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fetchIssueTableLayout(const QString &prefix)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(dd, return);
|
||||||
|
dd->fetchIssueTableLayout(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchIssues(const IssueListSearch &search)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(dd, return);
|
||||||
|
dd->fetchIssues(search);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Dto::ProjectInfoDto> projectInfo()
|
std::optional<Dto::ProjectInfoDto> projectInfo()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(dd, return {});
|
QTC_ASSERT(dd, return {});
|
||||||
@@ -332,6 +356,45 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName)
|
|||||||
m_taskTreeRunner.start(fetchDataRecipe<Dto::ProjectInfoDto>(url, handler));
|
m_taskTreeRunner.start(fetchDataRecipe<Dto::ProjectInfoDto>(url, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AxivionPluginPrivate::fetchIssueTableLayout(const QString &prefix)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_currentProjectInfo.has_value(), return);
|
||||||
|
if (m_taskTreeRunner.isRunning()) {
|
||||||
|
QTimer::singleShot(3000, this, [this, prefix] { fetchIssueTableLayout(prefix); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QUrl url = urlForProject(m_currentProjectInfo.value().name + '/')
|
||||||
|
.resolved(QString("issues_meta?kind=" + prefix));
|
||||||
|
|
||||||
|
const auto handler = [this](const Dto::TableInfoDto &data) {
|
||||||
|
m_axivionOutputPane.setTableDto(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_taskTreeRunner.start(fetchDataRecipe<Dto::TableInfoDto>(url, handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AxivionPluginPrivate::fetchIssues(const IssueListSearch &search)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_currentProjectInfo.has_value(), return);
|
||||||
|
if (m_taskTreeRunner.isRunning()) {
|
||||||
|
QTimer::singleShot(3000, this, [this, search] { fetchIssues(search); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString query = search.toQuery();
|
||||||
|
if (query.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QUrl url = urlForProject(m_currentProjectInfo.value().name + '/')
|
||||||
|
.resolved(QString("issues" + query));
|
||||||
|
|
||||||
|
const auto handler = [this](const Dto::IssueTableDto &data) {
|
||||||
|
m_axivionOutputPane.addIssues(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_taskTreeRunner.start(fetchDataRecipe<Dto::IssueTableDto>(url, handler));
|
||||||
|
}
|
||||||
|
|
||||||
void AxivionPluginPrivate::fetchRuleInfo(const QString &id)
|
void AxivionPluginPrivate::fetchRuleInfo(const QString &id)
|
||||||
{
|
{
|
||||||
if (m_runningQuery) {
|
if (m_runningQuery) {
|
||||||
|
@@ -15,8 +15,24 @@ namespace ProjectExplorer { class Project; }
|
|||||||
|
|
||||||
namespace Axivion::Internal {
|
namespace Axivion::Internal {
|
||||||
|
|
||||||
|
struct IssueListSearch
|
||||||
|
{
|
||||||
|
QString kind;
|
||||||
|
QString state;
|
||||||
|
QString versionStart;
|
||||||
|
QString versionEnd;
|
||||||
|
QString owner;
|
||||||
|
QString pathglob;
|
||||||
|
int offset = 0;
|
||||||
|
int limit = 30;
|
||||||
|
|
||||||
|
QString toQuery() const;
|
||||||
|
};
|
||||||
|
|
||||||
void fetchProjectInfo(const QString &projectName);
|
void fetchProjectInfo(const QString &projectName);
|
||||||
std::optional<Dto::ProjectInfoDto> projectInfo();
|
std::optional<Dto::ProjectInfoDto> projectInfo();
|
||||||
|
void fetchIssueTableLayout(const QString &prefix);
|
||||||
|
void fetchIssues(const IssueListSearch &search);
|
||||||
bool handleCertificateIssue();
|
bool handleCertificateIssue();
|
||||||
|
|
||||||
QIcon iconForIssue(const QString &prefix);
|
QIcon iconForIssue(const QString &prefix);
|
||||||
|
Reference in New Issue
Block a user