diff --git a/plugins/axivion/axivionoutputpane.cpp b/plugins/axivion/axivionoutputpane.cpp index d7896e02d0f..81df73cbc4f 100644 --- a/plugins/axivion/axivionoutputpane.cpp +++ b/plugins/axivion/axivionoutputpane.cpp @@ -89,4 +89,9 @@ void AxivionOutputPane::goToPrev() { } +void AxivionOutputPane::updateDashboard() +{ + +} + } // Axivion::Internal diff --git a/plugins/axivion/axivionoutputpane.h b/plugins/axivion/axivionoutputpane.h index 63ec48097ec..81b1647154c 100644 --- a/plugins/axivion/axivionoutputpane.h +++ b/plugins/axivion/axivionoutputpane.h @@ -33,6 +33,7 @@ public: void goToNext() override; void goToPrev() override; + void updateDashboard(); private: QStackedWidget *m_outputWidget = nullptr; }; diff --git a/plugins/axivion/axivionplugin.cpp b/plugins/axivion/axivionplugin.cpp index 62f4fa081be..f3e6cd8c184 100644 --- a/plugins/axivion/axivionplugin.cpp +++ b/plugins/axivion/axivionplugin.cpp @@ -5,14 +5,18 @@ #include "axivionoutputpane.h" #include "axivionprojectsettings.h" +#include "axivionquery.h" +#include "axivionresultparser.h" #include "axivionsettings.h" #include "axivionsettingspage.h" #include "axiviontr.h" #include +#include #include #include #include +#include #include #ifdef LICENSECHECKER @@ -20,16 +24,22 @@ #endif #include +#include namespace Axivion::Internal { -class AxivionPluginPrivate +class AxivionPluginPrivate : public QObject { public: + void fetchProjectInfo(const QString &projectName); + void handleProjectInfo(const ProjectInfo &info); + AxivionSettings axivionSettings; AxivionSettingsPage axivionSettingsPage{&axivionSettings}; AxivionOutputPane axivionOutputPane; QHash projectSettings; + ProjectInfo currentProjectInfo; + bool runningQuery = false; }; static AxivionPlugin *s_instance = nullptr; @@ -78,9 +88,26 @@ bool AxivionPlugin::initialize(const QStringList &arguments, QString *errorMessa return new AxivionProjectSettingsWidget(project); }); ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, &AxivionPlugin::onStartupProjectChanged); return true; } +void AxivionPlugin::onStartupProjectChanged() +{ + QTC_ASSERT(dd, return); + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + if (!project) { + dd->currentProjectInfo = ProjectInfo(); + dd->axivionOutputPane.updateDashboard(); + return; + } + + const AxivionProjectSettings *projSettings = projectSettings(project); + dd->fetchProjectInfo(projSettings->dashboardProjectName()); +} + AxivionSettings *AxivionPlugin::settings() { QTC_ASSERT(dd, return nullptr); @@ -116,4 +143,44 @@ bool AxivionPlugin::handleCertificateIssue() return true; } +void AxivionPlugin::fetchProjectInfo(const QString &projectName) +{ + QTC_ASSERT(dd, return); + dd->fetchProjectInfo(projectName); +} + +void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) +{ + if (runningQuery) { // re-schedule + QTimer::singleShot(3000, [this, projectName]{ fetchProjectInfo(projectName); }); + return; + } + if (projectName.isEmpty()) { + currentProjectInfo = ProjectInfo(); + axivionOutputPane.updateDashboard(); + return; + } + runningQuery = true; + + AxivionQuery query(AxivionQuery::ProjectInfo, {projectName}); + AxivionQueryRunner *runner = new AxivionQueryRunner(query, this); + connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){ + handleProjectInfo(ResultParser::parseProjectInfo(result)); + }); + connect(runner, &AxivionQueryRunner::finished, [runner]{ runner->deleteLater(); }); + runner->start(); +} + +void AxivionPluginPrivate::handleProjectInfo(const ProjectInfo &info) +{ + runningQuery = false; + if (!info.error.isEmpty()) { + Core::MessageManager::writeFlashing("Axivion: " + info.error); + return; + } + + currentProjectInfo = info; + axivionOutputPane.updateDashboard(); +} + } // Axivion::Internal diff --git a/plugins/axivion/axivionplugin.h b/plugins/axivion/axivionplugin.h index 0d71d005b96..fb0da44cbd0 100644 --- a/plugins/axivion/axivionplugin.h +++ b/plugins/axivion/axivionplugin.h @@ -26,6 +26,7 @@ public: static AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project); static bool handleCertificateIssue(); + static void fetchProjectInfo(const QString &projectName); signals: void settingsChanged(); @@ -33,6 +34,7 @@ signals: private: bool initialize(const QStringList &arguments, QString *errorMessage) final; void extensionsInitialized() final {} + void onStartupProjectChanged(); }; } // Axivion::Internal diff --git a/plugins/axivion/axivionprojectsettings.cpp b/plugins/axivion/axivionprojectsettings.cpp index 3d8b11c70dc..00844d10746 100644 --- a/plugins/axivion/axivionprojectsettings.cpp +++ b/plugins/axivion/axivionprojectsettings.cpp @@ -136,8 +136,10 @@ void AxivionProjectSettingsWidget::linkProject() const QList selected = m_dashboardProjects->selectedItems(); QTC_ASSERT(selected.size() == 1, return); - m_projectSettings->setDashboardProjectName(selected.first()->text(0)); + const QString projectName = selected.first()->text(0); + m_projectSettings->setDashboardProjectName(projectName); updateUi(); + AxivionPlugin::fetchProjectInfo(projectName); } void AxivionProjectSettingsWidget::unlinkProject() diff --git a/plugins/axivion/axivionquery.cpp b/plugins/axivion/axivionquery.cpp index 8b8cf48938f..4885b74ff46 100644 --- a/plugins/axivion/axivionquery.cpp +++ b/plugins/axivion/axivionquery.cpp @@ -9,6 +9,8 @@ #include #include +#include + using namespace Utils; namespace Axivion::Internal { @@ -27,6 +29,10 @@ QString AxivionQuery::toString() const return {}; case DashboardInfo: return query; + case ProjectInfo: + QTC_ASSERT(m_parameters.size() == 1, return {}); + query += "/projects/" + QUrl::toPercentEncoding(m_parameters.first()); + return query; } return {}; diff --git a/plugins/axivion/axivionquery.h b/plugins/axivion/axivionquery.h index 85721b07033..721700ab3c2 100644 --- a/plugins/axivion/axivionquery.h +++ b/plugins/axivion/axivionquery.h @@ -12,7 +12,7 @@ namespace Axivion::Internal { class AxivionQuery { public: - enum QueryType {NoQuery, DashboardInfo}; + enum QueryType {NoQuery, DashboardInfo, ProjectInfo}; explicit AxivionQuery(QueryType type, const QStringList ¶meters = {}); QString toString() const; diff --git a/plugins/axivion/axivionresultparser.cpp b/plugins/axivion/axivionresultparser.cpp index 5ef89a1cce1..4d765331623 100644 --- a/plugins/axivion/axivionresultparser.cpp +++ b/plugins/axivion/axivionresultparser.cpp @@ -70,6 +70,124 @@ static std::pair prehandleHeaderAndBody(const QByteAr return {result, doc}; } +static User::UserType userTypeForString(const QString &type) +{ + if (type == "DASHBOARD_USER") + return User::Dashboard; + if (type == "VIRTUAL_USER") + return User::Virtual; + return User::Unknown; +} + +static User userFromJson(const QJsonObject &object) +{ + User result; + if (object.isEmpty()) { + result.error = "Not a user object."; + return result; + } + result.name = object.value("name").toString(); + result.displayName = object.value("displayName").toString(); + result.type = userTypeForString(object.value("type").toString()); + return result; +} + +static QList usersFromJson(const QJsonArray &array) +{ + QList result; + for (const QJsonValue &value : array) { + User user = userFromJson(value.toObject()); + if (!user.error.isEmpty()) // add this error to result.error? + continue; + result.append(user); + } + return result; +} + +static IssueCount issueCountFromJson(const QJsonObject &object) +{ + IssueCount result; + if (object.isEmpty()) { + result.error = "Not an issue count object."; + return result; + } + result.added = object.value("Added").toInt(); + result.removed = object.value("Removed").toInt(); + result.total = object.value("Total").toInt(); + return result; +} + +static QList issueCountsFromJson(const QJsonObject &object) +{ + QList result; + + const QStringList keys = object.keys(); + for (const QString &k : keys) { + IssueCount issue = issueCountFromJson(object.value(k).toObject()); + if (!issue.error.isEmpty()) // add this error to result.error? + continue; + issue.issueKind = k; + result.append(issue); + } + return result; +} + +static ResultVersion versionFromJson(const QJsonObject &object) +{ + ResultVersion result; + if (object.isEmpty()) { + result.error = "Not a version object."; + return result; + } + const QJsonValue issuesValue = object.value("issueCounts"); + if (!issuesValue.isObject()) { + result.error = "Not an object (issueCounts)."; + return result; + } + result.issueCounts = issueCountsFromJson(issuesValue.toObject()); + result.timeStamp = object.value("date").toString(); + result.name = object.value("name").toString(); + result.linesOfCode = object.value("linesOfCode").toInt(); + return result; +} + +static QList versionsFromJson(const QJsonArray &array) +{ + QList result; + for (const QJsonValue &value : array) { + ResultVersion version = versionFromJson(value.toObject()); + if (!version.error.isEmpty()) // add this error to result.error? + continue; + result.append(version); + } + return result; +} + +static IssueKind issueKindFromJson(const QJsonObject &object) +{ + IssueKind result; + if (object.isEmpty()) { + result.error = "Not an issue kind object."; + return result; + } + result.prefix = object.value("prefix").toString(); + result.niceSingular = object.value("niceSingularName").toString(); + result.nicePlural = object.value("nicePluralName").toString(); + return result; +} + +static QList issueKindsFromJson(const QJsonArray &array) +{ + QList result; + for (const QJsonValue &value : array) { + IssueKind kind = issueKindFromJson(value.toObject()); + if (!kind.error.isEmpty()) // add this error to result.error? + continue; + result.append(kind); + } + return result; +} + namespace ResultParser { DashboardInfo parseDashboardInfo(const QByteArray &input) @@ -109,6 +227,43 @@ DashboardInfo parseDashboardInfo(const QByteArray &input) return result; } +ProjectInfo parseProjectInfo(const QByteArray &input) +{ + ProjectInfo result; + + auto [header, body] = splitHeaderAndBody(input); + auto [error, doc] = prehandleHeaderAndBody(header, body); + if (!error.error.isEmpty()) { + result.error = error.error; + return result; + } + + const QJsonObject object = doc.object(); + result.name = object.value("name").toString(); + + const QJsonValue usersValue = object.value("users"); + if (!usersValue.isArray()) { + result.error = "Malformed json response (users)."; + return result; + } + result.users = usersFromJson(usersValue.toArray()); + + const QJsonValue versionsValue = object.value("versions"); + if (!versionsValue.isArray()) { + result.error = "Malformed json response (versions)."; + return result; + } + result.versions = versionsFromJson(versionsValue.toArray()); + + const QJsonValue issueKindsValue = object.value("issueKinds"); + if (!issueKindsValue.isArray()) { + result.error = "Malformed json response (issueKinds)."; + return result; + } + result.issueKinds = issueKindsFromJson(issueKindsValue.toArray()); + return result; +} + } // ResultParser } // Axivion::Internal diff --git a/plugins/axivion/axivionresultparser.h b/plugins/axivion/axivionresultparser.h index 57a0127cc02..9a308f9950e 100644 --- a/plugins/axivion/axivionresultparser.h +++ b/plugins/axivion/axivionresultparser.h @@ -27,9 +27,53 @@ public: QList projects; }; +class User : public BaseResult +{ +public: + QString name; + QString displayName; + enum UserType { Dashboard, Virtual, Unknown } type; +}; + +class IssueKind : public BaseResult +{ +public: + QString prefix; + QString niceSingular; + QString nicePlural; +}; + +class IssueCount : public BaseResult +{ +public: + QString issueKind; + int total = 0; + int added = 0; + int removed = 0; +}; + +class ResultVersion : public BaseResult +{ +public: + QString name; + QString timeStamp; + QList issueCounts; + int linesOfCode = 0; +}; + +class ProjectInfo : public BaseResult +{ +public: + QString name; + QList users; + QList versions; + QList issueKinds; +}; + namespace ResultParser { DashboardInfo parseDashboardInfo(const QByteArray &input); +ProjectInfo parseProjectInfo(const QByteArray &input); } // ResultParser