feat(GODT-2791): Parse the Bug Report Flow description file and ensure forward compatibility (GODT-2789).

This commit is contained in:
Romain LE JEUNE
2023-07-25 21:07:59 +02:00
committed by Romain Le Jeune
parent 1a2783a63b
commit 86e115b2f3
7 changed files with 259 additions and 52 deletions

View File

@ -135,6 +135,7 @@ add_custom_command(
add_library(bridgepp
bridgepp/BugReportFlow/BugReportFlow.cpp bridgepp/BugReportFlow/BugReportFlow.h
bridgepp/BridgeUtils.cpp bridgepp/BridgeUtils.h
bridgepp/CLI/CLIUtils.cpp bridgepp/CLI/CLIUtils.h
bridgepp/Exception/Exception.h bridgepp/Exception/Exception.cpp

View File

@ -0,0 +1,172 @@
// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "BugReportFlow.h"
namespace {
QString const currentFormatVersion = "1.0.0";
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
BugReportFlow::BugReportFlow() {
}
//****************************************************************************************************************************************************
/// \param[in] filepath The path of the file to parse.
/// \return True iff the file can be properly parsed.
//****************************************************************************************************************************************************
bool BugReportFlow::parse(const QString& filepath) {
if (!QFile(filepath).exists())
return false;
this->filepath_ = filepath;
return parseFile();
}
//****************************************************************************************************************************************************
/// \param[in] categoryId The id of the bug category.
/// \return Set of question for this category.
//****************************************************************************************************************************************************
QVariantList BugReportFlow::questionSet(quint8 categoryId) const {
if (categoryId >= questionsSet_.count() - 1)
return QVariantList();
return questionsSet_[categoryId];
};
//****************************************************************************************************************************************************
/// \param[in] questionId The id of the question.
/// \param[in] answer The answer to that question.
/// \return true iff questionId match an existing question.
//****************************************************************************************************************************************************
bool BugReportFlow::setAnswer(quint8 questionId, QString const &answer) {
if (questionId >= questions_.count() - 1)
return false;
this->answers_[questionId] = answer;
return true;
}
//****************************************************************************************************************************************************
/// \param[in] categoryId The id of the question set.
/// \return concatenate answers for set of questions.
//****************************************************************************************************************************************************
QString BugReportFlow::collectAnswers(quint8 categoryId) const {
QString answers;
QVariantList sets = this->questionSet(categoryId);
foreach(const QVariant& var, sets) {
answers += " - ";
answers += questions_[var.toInt()].toMap()["text"].toString();
answers += " ";
answers += answers_[var.toInt()];
answers += "\n\r";
}
return answers;
}
//****************************************************************************************************************************************************
/// \return The value for the 'bugCategories' property.
//****************************************************************************************************************************************************
QStringList BugReportFlow::categories() const {
return categories_;
}
//****************************************************************************************************************************************************
/// \return The value for the 'bugQuestions' property.
//****************************************************************************************************************************************************
QVariantList BugReportFlow::questions() const {
return questions_;
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
bool BugReportFlow::parseFile() {
categories_.clear();
questions_.clear();
questionsSet_.clear();
QJsonObject data = getJsonDataObj(getJsonRootObj());
QJsonArray categoriesJson = data.value("categories").toArray();
foreach (const QJsonValue & v, categoriesJson) {
categories_.append(v.toObject()["name"].toString());
questionsSet_.append(v.toObject()["questions"].toArray().toVariantList());
}
questions_ = data.value("questions").toArray().toVariantList();
return true;
}
QJsonObject BugReportFlow::getJsonRootObj() {
QFile file(filepath_);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return QJsonObject();
const QString& val = file.readAll();
file.close();
QJsonDocument d = QJsonDocument::fromJson(val.toUtf8());
return d.object();
}
QJsonObject BugReportFlow::getJsonDataObj(const QJsonObject& root) {
QString version = getJsonVersion(root);
if (version.isEmpty())
return QJsonObject();
QJsonValue data = root.value(QString("data_v%1").arg(version));
if (data == QJsonValue::Undefined || !data.isObject())
return QJsonObject();
QJsonObject dataObj = data.toObject();
return migrateData(dataObj, version);
}
QString BugReportFlow::getJsonVersion(const QJsonObject& root) {
QJsonValue metadata = root.value("metadata");
if (metadata == QJsonValue::Undefined || !metadata.isObject()) {
return QString();
}
QJsonValue version = metadata.toObject().value("version");
if (version == QJsonValue::Undefined || !version.isString()) {
return QString();
}
return version.toString();
}
QJsonObject BugReportFlow::migrateData(const QJsonObject& data, const QString& version) {
if (version != currentFormatVersion)
return QJsonObject();
// nothing to migrate now but migration should be done here.
return data;
}

View File

@ -0,0 +1,57 @@
// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_BUG_REPORT_FLOW_H
#define BRIDGE_GUI_BUG_REPORT_FLOW_H
//****************************************************************************************************************************************************
/// \brief Bug Report Flow parser.
//****************************************************************************************************************************************************
class BugReportFlow {
public: // member functions.
BugReportFlow(); ///< Default constructor.
BugReportFlow(BugReportFlow const &) = delete; ///< Disabled copy-constructor.
BugReportFlow(BugReportFlow &&) = delete; ///< Disabled assignment copy-constructor.
~BugReportFlow() = default; ///< Destructor.
bool parse(const QString& filepath); ///< Initialize the Bug Report Flow.
QVariantList questionSet(quint8 categoryId) const; ///< Retrieve the set of question for a given bug category.
bool setAnswer(quint8 questionId, QString const &answer); ///< Feed an answer for a given question.
QString collectAnswers(quint8 categoryId) const; ///< Collect answer for a given set of questions.
QStringList categories() const; ///< Getter for the 'bugCategories' property.
QVariantList questions() const; ///< Getter for the 'bugQuestions' property.
private: // member functions
bool parseFile(); ///< Parse the bug report flow description file.
QJsonObject getJsonRootObj(); ///< Extract the JSON root object.
QJsonObject getJsonDataObj(const QJsonObject& root); ///< Extract the JSON data object.
QString getJsonVersion(const QJsonObject& root); ///< Extract the JSON version of the file.
QJsonObject migrateData(const QJsonObject& data, const QString& version); ///< Migrate data if needed/possible.
private: // data members
QString filepath_; ///< The file path of the BugReportFlow description file.
QStringList categories_; ///< The list of Bug Category parsed from the description file.
QVariantList questions_; ///< The list of Questions parsed from the description file.
QList<QVariantList> questionsSet_; ///< Sets of questions per bug category.
QMap<quint8, QString> answers_; ///< Map of QuestionId/Answer for the bug form.
};
#endif // BRIDGE_GUI_BUG_REPORT_FLOW_H