Basic JSON RESTful client fetching items from a local server at application start and adding them to the model.

This commit is contained in:
2026-01-25 10:47:19 +01:00
parent e1bc779791
commit e29cd0aebf
8 changed files with 178 additions and 3 deletions

View File

@ -12,6 +12,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core LinguistTools Gui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Test)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network)
configure_file(CoreConfig.h.in CoreConfig.h)
@ -32,16 +33,18 @@ add_library(${TARGET_APP} STATIC
data/filehandler.h data/filehandler.cpp
model/metadata.h
formats/csvparser.h formats/csvparser.cpp
model/generalsortfiltermodel.h model/generalsortfiltermodel.cpp
network/servercommunicator.h network/servercommunicator.cpp
network/apiroutes.h
# 3rd party libraries
../3rdParty/rapidcsv/src/rapidcsv.h
model/generalsortfiltermodel.h model/generalsortfiltermodel.cpp
)
target_link_libraries(GenericCore PRIVATE Qt${QT_VERSION_MAJOR}::Test)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui)
target_link_libraries(GenericCore PRIVATE Qt${QT_VERSION_MAJOR}::Test)
target_link_libraries(GenericCore PRIVATE Qt${QT_VERSION_MAJOR}::Network)
target_compile_definitions(${TARGET_APP} PRIVATE ${TARGET_APP}_LIBRARY)

View File

@ -16,6 +16,7 @@
#include "model/generalsortfiltermodel.h"
#include "model/metadata.h"
#include "model/tablemodel.h"
#include "network/servercommunicator.h"
#include <QtGui/QUndoStack>
@ -37,6 +38,7 @@ GenericCore::GenericCore() {
m_modelUndoStack = new QUndoStack(this);
setupModels();
setupServerConfiguration();
}
GenericCore::~GenericCore() { qDebug() << "Destroying core..."; }
@ -132,6 +134,32 @@ bool GenericCore::exportCSVFile(const QString& filePath) {
return FileHandler::exportToCSVFile(itemsAsStringLists, filePath);
}
bool GenericCore::isSyncServerSetup() const {
if (m_serverCommunicator) {
return true;
} else {
return false;
}
}
void GenericCore::onItemsFetched(const QByteArray jsonDoc) {
emit displayStatusMessage("New items fetched.");
// TODO ? check compability of JSON structure beforehand?
m_mainModel->appendItems(jsonDoc);
}
void GenericCore::onItemsFetchFailure(const QString errorString) {
emit displayStatusMessage(QString("Error: %1").arg(errorString));
}
void GenericCore::onPostRequestSuccessful(const QString message) {
emit displayStatusMessage(message);
}
void GenericCore::onPostRequestFailure(const QString errorString) {
emit displayStatusMessage(QString("Error: %1").arg(errorString));
}
void GenericCore::setupModels() {
m_mainModel = make_shared<TableModel>(m_modelUndoStack, this);
m_sortFilterModel = make_shared<GeneralSortFilterModel>(m_mainModel);
@ -188,3 +216,19 @@ QString GenericCore::getMaintenanceToolFilePath() const {
const QString filePath = applicationDirPath + "/" + UPDATER_EXE;
return filePath;
}
void GenericCore::setupServerConfiguration() {
m_serverCommunicator = make_unique<ServerCommunicator>(this);
connect(m_serverCommunicator.get(), &ServerCommunicator::itemsFetched, this,
&GenericCore::onItemsFetched);
connect(m_serverCommunicator.get(), &ServerCommunicator::itemsFetchFailure, this,
&GenericCore::onItemsFetchFailure);
connect(m_serverCommunicator.get(), &ServerCommunicator::postRequestSuccessful, this,
&GenericCore::onPostRequestSuccessful);
connect(m_serverCommunicator.get(), &ServerCommunicator::postRequestFailure, this,
&GenericCore::onPostRequestFailure);
/// fetching items on startup to test the communication with the server
m_serverCommunicator->fetchItems();
}

View File

@ -10,6 +10,7 @@ class QString;
class TableModel;
class GeneralSortFilterModel;
class ServerCommunicator;
class GenericCore : public QObject {
Q_OBJECT
@ -32,6 +33,14 @@ class GenericCore : public QObject {
void importCSVFile(const QString& filePath);
bool exportCSVFile(const QString& filePath);
bool isSyncServerSetup() const;
public slots:
void onItemsFetched(const QByteArray jsonDoc);
void onItemsFetchFailure(const QString errorString);
void onPostRequestSuccessful(const QString message);
void onPostRequestFailure(const QString errorString);
signals:
void displayStatusMessage(QString message);
@ -46,6 +55,10 @@ class GenericCore : public QObject {
void initModelData();
QString getMaintenanceToolFilePath() const;
/// Network communication
std::unique_ptr<ServerCommunicator> m_serverCommunicator;
void setupServerConfiguration();
};
#endif // GENERICCORE_H

View File

@ -6,6 +6,8 @@
#include <QList>
#include <QString>
// TODO add namespace
/// model data
enum UserRoles {
NameRole = Qt::UserRole + 1,
@ -16,6 +18,7 @@ enum UserRoles {
/// helper roles
ToStringRole
};
static UserRoles DEFAULT_ROLE = NameRole;
static QList<UserRoles> USER_FACING_ROLES = {NameRole, DescriptionRole, InfoRole, AmountRole,
FactorRole};

View File

@ -193,6 +193,12 @@ void TableModel::insertItems(int startPosition,
const QModelIndex& parentIndex) {
const QList<ModelItemValues> valueList = JsonParser::toItemValuesList(jsonDoc, ITEM_KEY_STRING);
if (valueList.empty()) {
/// don't try inserting if no values to insert
qDebug() << "No items found in JSON document. Not adding anything...";
return;
}
insertItems(startPosition, valueList, parentIndex);
}

13
network/apiroutes.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef APIROUTES_H
#define APIROUTES_H
#include <QString>
// TODO add namespace
static const QString baseUrl = "http://127.0.0.1:4000";
static const QString apiPrefix = "/api/";
static const QString ROUTE_ITEMS = apiPrefix + "items";
#endif // APIROUTES_H

View File

@ -0,0 +1,60 @@
#include "servercommunicator.h"
#include "apiroutes.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRestReply>
using namespace Qt::StringLiterals;
ServerCommunicator::ServerCommunicator(QObject* parent)
: QObject{parent} {
m_netManager.setAutoDeleteReplies(true);
m_restManager = std::make_shared<QRestAccessManager>(&m_netManager);
m_serviceApi = std::make_shared<QNetworkRequestFactory>();
setUrl(baseUrl);
}
bool ServerCommunicator::sslSupported() {
#if QT_CONFIG(ssl)
return QSslSocket::supportsSsl();
#else
return false;
#endif
}
QUrl ServerCommunicator::url() const { return m_serviceApi->baseUrl(); }
void ServerCommunicator::setUrl(const QUrl& url) {
if (m_serviceApi->baseUrl() == url) {
return;
}
m_serviceApi->setBaseUrl(url);
QHttpHeaders authenticationHeaders;
m_serviceApi->setCommonHeaders(authenticationHeaders);
emit urlChanged();
}
void ServerCommunicator::fetchItems() {
/// Set up a GET request
m_restManager->get(m_serviceApi->createRequest(ROUTE_ITEMS), this, [this](QRestReply& reply) {
if (reply.isSuccess()) {
qInfo() << "Fetching items successful.";
const QJsonDocument doc = reply.readJson().value();
emit itemsFetched(doc.toJson());
} else {
if (reply.hasError()) {
const QString errorString = reply.errorString();
qCritical() << "ERROR:" << errorString;
emit itemsFetchFailure(errorString);
} else {
int statusCode = reply.httpStatus();
qCritical() << "ERROR:" << statusCode;
emit itemsFetchFailure(QString::number(statusCode));
emit itemsFetchFailure(QString("HTTP status code: %1").arg(statusCode));
}
}
});
}

View File

@ -0,0 +1,33 @@
#ifndef SERVERCOMMUNICATOR_H
#define SERVERCOMMUNICATOR_H
#include <QNetworkAccessManager>
#include <QNetworkRequestFactory>
#include <QObject>
#include <QRestAccessManager>
class ServerCommunicator : public QObject {
Q_OBJECT
public:
explicit ServerCommunicator(QObject* parent = nullptr);
bool sslSupported();
QUrl url() const;
void setUrl(const QUrl& url);
void fetchItems();
signals:
void urlChanged();
void itemsFetched(const QByteArray jsonDoc);
void itemsFetchFailure(const QString errorString);
private:
QNetworkAccessManager m_netManager;
std::shared_ptr<QRestAccessManager> m_restManager;
std::shared_ptr<QNetworkRequestFactory> m_serviceApi;
};
#endif // SERVERCOMMUNICATOR_H