Basic JSON RESTful client fetching items from a local server at application start and adding them to the model.
This commit is contained in:
@ -12,6 +12,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core LinguistTools)
|
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 Core LinguistTools Gui)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Test)
|
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)
|
configure_file(CoreConfig.h.in CoreConfig.h)
|
||||||
|
|
||||||
@ -32,16 +33,18 @@ add_library(${TARGET_APP} STATIC
|
|||||||
data/filehandler.h data/filehandler.cpp
|
data/filehandler.h data/filehandler.cpp
|
||||||
model/metadata.h
|
model/metadata.h
|
||||||
formats/csvparser.h formats/csvparser.cpp
|
formats/csvparser.h formats/csvparser.cpp
|
||||||
|
model/generalsortfiltermodel.h model/generalsortfiltermodel.cpp
|
||||||
|
network/servercommunicator.h network/servercommunicator.cpp
|
||||||
|
network/apiroutes.h
|
||||||
# 3rd party libraries
|
# 3rd party libraries
|
||||||
../3rdParty/rapidcsv/src/rapidcsv.h
|
../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})
|
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(${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)
|
target_compile_definitions(${TARGET_APP} PRIVATE ${TARGET_APP}_LIBRARY)
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "model/generalsortfiltermodel.h"
|
#include "model/generalsortfiltermodel.h"
|
||||||
#include "model/metadata.h"
|
#include "model/metadata.h"
|
||||||
#include "model/tablemodel.h"
|
#include "model/tablemodel.h"
|
||||||
|
#include "network/servercommunicator.h"
|
||||||
|
|
||||||
#include <QtGui/QUndoStack>
|
#include <QtGui/QUndoStack>
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ GenericCore::GenericCore() {
|
|||||||
m_modelUndoStack = new QUndoStack(this);
|
m_modelUndoStack = new QUndoStack(this);
|
||||||
|
|
||||||
setupModels();
|
setupModels();
|
||||||
|
setupServerConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericCore::~GenericCore() { qDebug() << "Destroying core..."; }
|
GenericCore::~GenericCore() { qDebug() << "Destroying core..."; }
|
||||||
@ -132,6 +134,32 @@ bool GenericCore::exportCSVFile(const QString& filePath) {
|
|||||||
return FileHandler::exportToCSVFile(itemsAsStringLists, 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() {
|
void GenericCore::setupModels() {
|
||||||
m_mainModel = make_shared<TableModel>(m_modelUndoStack, this);
|
m_mainModel = make_shared<TableModel>(m_modelUndoStack, this);
|
||||||
m_sortFilterModel = make_shared<GeneralSortFilterModel>(m_mainModel);
|
m_sortFilterModel = make_shared<GeneralSortFilterModel>(m_mainModel);
|
||||||
@ -188,3 +216,19 @@ QString GenericCore::getMaintenanceToolFilePath() const {
|
|||||||
const QString filePath = applicationDirPath + "/" + UPDATER_EXE;
|
const QString filePath = applicationDirPath + "/" + UPDATER_EXE;
|
||||||
return filePath;
|
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();
|
||||||
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ class QString;
|
|||||||
|
|
||||||
class TableModel;
|
class TableModel;
|
||||||
class GeneralSortFilterModel;
|
class GeneralSortFilterModel;
|
||||||
|
class ServerCommunicator;
|
||||||
|
|
||||||
class GenericCore : public QObject {
|
class GenericCore : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -32,6 +33,14 @@ class GenericCore : public QObject {
|
|||||||
void importCSVFile(const QString& filePath);
|
void importCSVFile(const QString& filePath);
|
||||||
bool exportCSVFile(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:
|
signals:
|
||||||
void displayStatusMessage(QString message);
|
void displayStatusMessage(QString message);
|
||||||
|
|
||||||
@ -46,6 +55,10 @@ class GenericCore : public QObject {
|
|||||||
void initModelData();
|
void initModelData();
|
||||||
|
|
||||||
QString getMaintenanceToolFilePath() const;
|
QString getMaintenanceToolFilePath() const;
|
||||||
|
|
||||||
|
/// Network communication
|
||||||
|
std::unique_ptr<ServerCommunicator> m_serverCommunicator;
|
||||||
|
void setupServerConfiguration();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GENERICCORE_H
|
#endif // GENERICCORE_H
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
// TODO add namespace
|
||||||
|
|
||||||
/// model data
|
/// model data
|
||||||
enum UserRoles {
|
enum UserRoles {
|
||||||
NameRole = Qt::UserRole + 1,
|
NameRole = Qt::UserRole + 1,
|
||||||
@ -16,6 +18,7 @@ enum UserRoles {
|
|||||||
/// helper roles
|
/// helper roles
|
||||||
ToStringRole
|
ToStringRole
|
||||||
};
|
};
|
||||||
|
|
||||||
static UserRoles DEFAULT_ROLE = NameRole;
|
static UserRoles DEFAULT_ROLE = NameRole;
|
||||||
static QList<UserRoles> USER_FACING_ROLES = {NameRole, DescriptionRole, InfoRole, AmountRole,
|
static QList<UserRoles> USER_FACING_ROLES = {NameRole, DescriptionRole, InfoRole, AmountRole,
|
||||||
FactorRole};
|
FactorRole};
|
||||||
|
|||||||
@ -193,6 +193,12 @@ void TableModel::insertItems(int startPosition,
|
|||||||
const QModelIndex& parentIndex) {
|
const QModelIndex& parentIndex) {
|
||||||
const QList<ModelItemValues> valueList = JsonParser::toItemValuesList(jsonDoc, ITEM_KEY_STRING);
|
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);
|
insertItems(startPosition, valueList, parentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
network/apiroutes.h
Normal file
13
network/apiroutes.h
Normal 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
|
||||||
60
network/servercommunicator.cpp
Normal file
60
network/servercommunicator.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
33
network/servercommunicator.h
Normal file
33
network/servercommunicator.h
Normal 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
|
||||||
Reference in New Issue
Block a user