mirror of
https://gitlab.com/spaceti-app/integrations/common/server-sdk.git
synced 2026-07-12 17:50:41 +02:00
initial commit
This commit is contained in:
commit
76dd2cd294
32
.gitlab-ci.yml
Normal file
32
.gitlab-ci.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# for
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
|
||||||
|
# ########
|
||||||
|
# Build
|
||||||
|
##########
|
||||||
|
build: # name of task
|
||||||
|
stage: build # in what stage it will run ( 1. build then test etc )
|
||||||
|
tags: # Tags are used to choose correct gitlab-runner
|
||||||
|
- hw # spaceti-runner server has running runners and one has tag 'hw'
|
||||||
|
script: # run in bash
|
||||||
|
- mkdir -p build
|
||||||
|
- cd build
|
||||||
|
- qmake ../core-sdk.pro
|
||||||
|
- make -j8
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- build/
|
||||||
|
|
||||||
|
|
||||||
|
# ########
|
||||||
|
# Test
|
||||||
|
##########
|
||||||
|
test: # name of task
|
||||||
|
stage: test # in what stage it will run ( 1. build then test etc )
|
||||||
|
tags: # Tags are used to choose correct gitlab-runner
|
||||||
|
- hw # spaceti-runner server has running runners and one has tag 'hw'
|
||||||
|
script: # run in bash
|
||||||
|
- cd build
|
||||||
|
- make check test
|
||||||
10
core-sdk.pro
Normal file
10
core-sdk.pro
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS += \
|
||||||
|
src/lib \
|
||||||
|
src/testapp \
|
||||||
|
test \
|
||||||
|
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
.gitlab-ci.yml
|
||||||
113
src/core/compression.cpp
Normal file
113
src/core/compression.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include "compression.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include <zlib.h> // gzip decompress
|
||||||
|
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decompress gzip data
|
||||||
|
* @param compressed gzip data
|
||||||
|
* @return decompressed data
|
||||||
|
*/
|
||||||
|
QByteArray Compression::decompress(const QByteArray &compressed)
|
||||||
|
{
|
||||||
|
if (compressed.size() <= 4) {
|
||||||
|
qWarning("gUncompress: Input data is truncated");
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray result;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
z_stream strm;
|
||||||
|
static const int CHUNK_SIZE = 1024;
|
||||||
|
char out[CHUNK_SIZE];
|
||||||
|
|
||||||
|
/* allocate inflate state */
|
||||||
|
strm.zalloc = Z_NULL;
|
||||||
|
strm.zfree = Z_NULL;
|
||||||
|
strm.opaque = Z_NULL;
|
||||||
|
strm.avail_in = compressed.size();
|
||||||
|
strm.next_in = (Bytef*)(compressed.data());
|
||||||
|
|
||||||
|
ret = inflateInit2(&strm, 15 + 32); // gzip decoding
|
||||||
|
if (ret != Z_OK)
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
// run inflate()
|
||||||
|
do {
|
||||||
|
strm.avail_out = CHUNK_SIZE;
|
||||||
|
strm.next_out = (Bytef*)(out);
|
||||||
|
|
||||||
|
ret = inflate(&strm, Z_NO_FLUSH);
|
||||||
|
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
return QByteArray();
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
(void)inflateEnd(&strm);
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(out, CHUNK_SIZE - strm.avail_out);
|
||||||
|
} while (strm.avail_out == 0);
|
||||||
|
|
||||||
|
// clean up and return
|
||||||
|
inflateEnd(&strm);
|
||||||
|
|
||||||
|
// Calculate compression ratio
|
||||||
|
// original :200
|
||||||
|
// compressed :100
|
||||||
|
// ratio = 0.5
|
||||||
|
float ratio = (float)compressed.count() / (float)result.count();
|
||||||
|
float compression = ratio * 100.0;
|
||||||
|
|
||||||
|
qCInfo(sdkcompress) << "compress data:" << compressed.count()
|
||||||
|
<< "uncompressed data:" << result.count()
|
||||||
|
<< "compression:" << compression;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return path to source directory
|
||||||
|
* @param subpath ( e.g. test/data )
|
||||||
|
* @return absolute path to desired subpath in source folder
|
||||||
|
*/
|
||||||
|
QString Compression::sourcePath(const QString &subpath)
|
||||||
|
{
|
||||||
|
QDir dir(__FILE__);
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
|
||||||
|
return dir.absoluteFilePath(subpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return data from source directory
|
||||||
|
* @param subpath ( e.g. test/data/gizp.src )
|
||||||
|
* @return data of file - QByteArray
|
||||||
|
*/
|
||||||
|
QByteArray Compression::sourceData(const QString &subpath)
|
||||||
|
{
|
||||||
|
// Get data from file
|
||||||
|
QString filePath = sourcePath(subpath);
|
||||||
|
if (! QFileInfo::exists(filePath)) {
|
||||||
|
qCWarning(sdkcompress) << "path does not exists:" << filePath;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
QFile file(filePath);
|
||||||
|
if ( file.open(QIODevice::ReadOnly) )
|
||||||
|
return file.readAll();
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
19
src/core/compression.h
Normal file
19
src/core/compression.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class Compression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QByteArray decompress(const QByteArray &compressed);
|
||||||
|
|
||||||
|
static QString sourcePath(const QString &subpath);
|
||||||
|
static QByteArray sourceData(const QString &subpath);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
|
|
||||||
21
src/core/core.pri
Normal file
21
src/core/core.pri
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
CONFIG += c++14
|
||||||
|
#SRC_DIR = $$PWD
|
||||||
|
message($$PWD)
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/databasemanager.cpp \
|
||||||
|
$$PWD/logger.cpp \
|
||||||
|
$$PWD/loggermessage.cpp \
|
||||||
|
$$PWD/requestmanager.cpp \
|
||||||
|
$$PWD/request.cpp \
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/databasemanager.h \
|
||||||
|
$$PWD/logger.h \
|
||||||
|
$$PWD/loggermessage.h \
|
||||||
|
$$PWD/requestmanager.h \
|
||||||
|
$$PWD/request.h \
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: implement zlib compression
|
||||||
|
# include (zlib.pri)
|
||||||
56
src/core/databasemanager.cpp
Normal file
56
src/core/databasemanager.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#include "databasemanager.h"
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
DatabaseManager::DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool dryMode)
|
||||||
|
: mDryMode(dryMode)
|
||||||
|
{
|
||||||
|
db = QSqlDatabase::addDatabase("QPSQL");
|
||||||
|
db.setDatabaseName(dbName);
|
||||||
|
db.setHostName(host);
|
||||||
|
db.setUserName(user); // home-office stuff -->> db.setPort(80);
|
||||||
|
db.setPassword(password);
|
||||||
|
if (db.open())
|
||||||
|
{
|
||||||
|
query = QSqlQuery("query", db);
|
||||||
|
} else {
|
||||||
|
log("Opening error" + db.lastError().text());
|
||||||
|
emit error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::executeQuery(QSqlQuery &query)
|
||||||
|
{
|
||||||
|
if (mDryMode){
|
||||||
|
QString sqlStatement = getLastQuery(query);
|
||||||
|
qDebug() << sqlStatement;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!mDryMode || query.isSelect()){
|
||||||
|
if (!query.exec()){
|
||||||
|
QString msg = QString("Source: Database Manager, Error: %1").arg(query.lastError().text());
|
||||||
|
log(QString(msg));
|
||||||
|
emit error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseManager::getLastQuery(const QSqlQuery& query) const
|
||||||
|
{
|
||||||
|
QString str = query.lastQuery();
|
||||||
|
QMapIterator<QString, QVariant> it(query.boundValues());
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
it.next();
|
||||||
|
str.replace(it.key(),it.value().toString());
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::log(const QString &msg) const
|
||||||
|
{
|
||||||
|
qDebug("database logger implement logger");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // core
|
||||||
37
src/core/databasemanager.h
Normal file
37
src/core/databasemanager.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSql>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDateTime>
|
||||||
|
//#include "logger.h"
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class DatabaseManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class Logger;
|
||||||
|
public:
|
||||||
|
DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool mDryMode = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool executeQuery(QSqlQuery &query);
|
||||||
|
QString getLastQuery(const QSqlQuery &query) const;
|
||||||
|
|
||||||
|
void log(const QString &msg) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void error();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSqlDatabase db;
|
||||||
|
QSqlQuery query;
|
||||||
|
bool mDryMode = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // core
|
||||||
104
src/core/logger.cpp
Normal file
104
src/core/logger.cpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#include "logger.h"
|
||||||
|
#include "loggermessage.h"
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(sdkcore, "sdk.core");
|
||||||
|
Q_LOGGING_CATEGORY(sdknetwork, "sdk.network");
|
||||||
|
Q_LOGGING_CATEGORY(sdkcompress, "sdk.compress");
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
Logger* Logger::sConsumer = nullptr;
|
||||||
|
//bool Logger::sShowFunction = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Used to have user specific consumer of logger messages
|
||||||
|
* @param consumer - inv::Logger
|
||||||
|
*/
|
||||||
|
void Logger::setConsumer(Logger *consumer)
|
||||||
|
{
|
||||||
|
sConsumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::Logger(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<LoggerMessage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LoggerMessage> Logger::getNewMessages(int currentCount)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mMutex);
|
||||||
|
QList<LoggerMessage> list;
|
||||||
|
|
||||||
|
int count = mLogMessages.count();
|
||||||
|
int total = count - currentCount;
|
||||||
|
if (total <= 0)
|
||||||
|
return list;
|
||||||
|
|
||||||
|
for (int i = 0; i < total; ++i) {
|
||||||
|
int currentIndex = currentCount;
|
||||||
|
int row = i + currentIndex;
|
||||||
|
list.append(mLogMessages.at(row));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
{
|
||||||
|
QString shortFile = QFileInfo(context.file).fileName();
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
QString textType;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case QtDebugMsg:
|
||||||
|
textType = QStringLiteral("debug");
|
||||||
|
break;
|
||||||
|
case QtWarningMsg:
|
||||||
|
textType = QStringLiteral("warning");
|
||||||
|
break;
|
||||||
|
case QtCriticalMsg:
|
||||||
|
textType = QStringLiteral("critical");
|
||||||
|
break;
|
||||||
|
case QtFatalMsg:
|
||||||
|
textType = QStringLiteral("fatal");
|
||||||
|
break;
|
||||||
|
#if QT_VERSION >= 0x050500
|
||||||
|
case QtInfoMsg:
|
||||||
|
textType = QStringLiteral("info");
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (sConsumer) {
|
||||||
|
LoggerMessage logmsg(shortFile,context.line,context.function,textType,context.category,msg, QLatin1String(""));
|
||||||
|
sConsumer->insertMessage(logmsg); // emiting message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output message
|
||||||
|
//text += QString("%1 %2:%3 %4").arg(context.category).arg(shortFile).arg(context.line).arg(msg);
|
||||||
|
// New output syntax
|
||||||
|
text = QStringLiteral("[%1] %2 [ %3:%4 | %5 | %6 ]")
|
||||||
|
.arg(textType, msg, shortFile)
|
||||||
|
.arg(context.line)
|
||||||
|
.arg(context.function, context.category);
|
||||||
|
|
||||||
|
QTextStream out(stderr);
|
||||||
|
out << text << "\n"; //.toLocal8Bit() + "\n";
|
||||||
|
//out.flush(); // INVSDK-37 ORK-102 crash on windows when flush() -> possible threading problem
|
||||||
|
|
||||||
|
if (type == QtFatalMsg)
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::insertMessage(const LoggerMessage& logmsg)
|
||||||
|
{
|
||||||
|
emit sConsumer->messageReceived(logmsg);
|
||||||
|
QMutexLocker locker(&sConsumer->mMutex);
|
||||||
|
sConsumer->mLogMessages.append(logmsg);
|
||||||
|
emit sConsumer->messageListUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // core
|
||||||
|
|
||||||
42
src/core/logger.h
Normal file
42
src/core/logger.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "loggermessage.h"
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QObject>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(sdkcore)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(sdknetwork)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(sdkcompress)
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/** @see http://stackoverflow.com/questions/8337300/c11-how-do-i-implement-convenient-logging-without-a-singleton */
|
||||||
|
class Logger : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
// Static methods
|
||||||
|
static Logger* sConsumer;
|
||||||
|
static void setConsumer(Logger *consumer);
|
||||||
|
static void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||||
|
// static void message(const char *file, int line, const char *function, const QString &type, const QString &group, const QString &msg, const QString &detail = QString());
|
||||||
|
// Public methods
|
||||||
|
explicit Logger(QObject *parent = nullptr);
|
||||||
|
void insertMessage(const LoggerMessage& logmsg);
|
||||||
|
QList<core::LoggerMessage> getNewMessages(int currentCount);
|
||||||
|
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void messageReceived(const core::LoggerMessage &message);
|
||||||
|
void messageListUpdated();
|
||||||
|
private:
|
||||||
|
QList<LoggerMessage> mLogMessages;
|
||||||
|
QMutex mMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // core
|
||||||
64
src/core/loggermessage.cpp
Normal file
64
src/core/loggermessage.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "loggermessage.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
LoggerMessage::LoggerMessage(QString file, int line, QString function, QString type, QString group, QString msg, QString detailMessage)
|
||||||
|
: mFile(std::move(file)), mFunction(std::move(function)), mType(std::move(type)), mLine(line),
|
||||||
|
mGroup(std::move(group)), mMessage(std::move(msg)), mDetailMessage(std::move(detailMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::shortFile() const
|
||||||
|
{
|
||||||
|
return QFileInfo(mFile).fileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::type() const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::file() const
|
||||||
|
{
|
||||||
|
return mFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LoggerMessage::line() const
|
||||||
|
{
|
||||||
|
return mLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::group() const
|
||||||
|
{
|
||||||
|
return mGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::message() const
|
||||||
|
{
|
||||||
|
return mMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::detail() const
|
||||||
|
{
|
||||||
|
return mDetailMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoggerMessage::function() const
|
||||||
|
{
|
||||||
|
return mFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // core
|
||||||
|
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const core::LoggerMessage &message)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "message: " << message.message();
|
||||||
|
dbg.nospace() << "next output not implemented: " << message.message();
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
36
src/core/loggermessage.h
Normal file
36
src/core/loggermessage.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class LoggerMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LoggerMessage() = default;
|
||||||
|
LoggerMessage(QString file, int line, QString function, QString type, QString group, QString msg, QString detailMessage = QString());
|
||||||
|
QString shortFile() const;
|
||||||
|
QString type() const;
|
||||||
|
QString file() const;
|
||||||
|
int line() const;
|
||||||
|
QString group() const;
|
||||||
|
QString message() const;
|
||||||
|
QString detail() const;
|
||||||
|
QString function() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mFile;
|
||||||
|
QString mFunction;
|
||||||
|
QString mType;
|
||||||
|
int mLine = -1;
|
||||||
|
QString mGroup;
|
||||||
|
QString mMessage;
|
||||||
|
QString mDetailMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // core
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const core::LoggerMessage &message);
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(core::LoggerMessage)
|
||||||
103
src/core/request.cpp
Normal file
103
src/core/request.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include "request.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "requestmanager.h" // to get logger class
|
||||||
|
#include <QDebug>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
|
||||||
|
Request::~Request()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QNetworkRequest Request::networkRequest() const
|
||||||
|
{
|
||||||
|
QNetworkRequest request(QUrl(this->mUrl));
|
||||||
|
|
||||||
|
if ( !userName().isEmpty() && !password().isEmpty() )
|
||||||
|
request.setRawHeader("Authorization", this->basicAuthorization().toUtf8());
|
||||||
|
|
||||||
|
if ( !token().isEmpty() ) {
|
||||||
|
request.setRawHeader("Authorization", this->bearerAuthorization().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled compression
|
||||||
|
#ifdef CORESDK_ZLIB
|
||||||
|
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enabling HTTP/2 ( not supported on Ubuntu 16.04 )
|
||||||
|
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Request::appendData(const QByteArray &array)
|
||||||
|
{
|
||||||
|
mResponseData += array;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Request::responseData() const
|
||||||
|
{
|
||||||
|
return mResponseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Request::requestData() const
|
||||||
|
{
|
||||||
|
return mRequestData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setRequestData(const QByteArray &requestData)
|
||||||
|
{
|
||||||
|
mRequestData = requestData;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Request::token() const
|
||||||
|
{
|
||||||
|
return mToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setToken(const QString &token)
|
||||||
|
{
|
||||||
|
mToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Request::userName() const
|
||||||
|
{
|
||||||
|
return mUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setUserName(const QString &username)
|
||||||
|
{
|
||||||
|
mUserName = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Request::password() const
|
||||||
|
{
|
||||||
|
return mPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
mPassword = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Request::basicAuthorization() const
|
||||||
|
{
|
||||||
|
// HTTP Basic authentication header value: base64(username:password)
|
||||||
|
QString concatenated = userName() + ":" + password();
|
||||||
|
QByteArray data = concatenated.toLocal8Bit().toBase64();
|
||||||
|
QString headerData = "Basic " + data;
|
||||||
|
return headerData;
|
||||||
|
//request.setRawHeader("Authorization", headerData.toLocal8Bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Request::bearerAuthorization() const
|
||||||
|
{
|
||||||
|
qCDebug(sdknetwork) << "not implemented yet";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // core
|
||||||
58
src/core/request.h
Normal file
58
src/core/request.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class Request : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
friend class RequestManager;
|
||||||
|
friend class RequestTest;
|
||||||
|
public:
|
||||||
|
Request() = default;
|
||||||
|
~Request() override;
|
||||||
|
Request(QString type, QString url, QByteArray responseData = QByteArray());
|
||||||
|
QNetworkRequest networkRequest() const;
|
||||||
|
|
||||||
|
// Data handling data
|
||||||
|
QByteArray requestData() const;
|
||||||
|
void setRequestData(const QByteArray &requestData);
|
||||||
|
QByteArray responseData() const;
|
||||||
|
|
||||||
|
|
||||||
|
// Authorization
|
||||||
|
QString userName() const;
|
||||||
|
void setUserName(const QString &userName);
|
||||||
|
QString password() const;
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
QString token() const;
|
||||||
|
void setToken(const QString &token);
|
||||||
|
|
||||||
|
// Authorization headers
|
||||||
|
QString basicAuthorization() const;
|
||||||
|
QString bearerAuthorization() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Data handling
|
||||||
|
void appendData(const QByteArray &array);
|
||||||
|
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mType; // POST, GET
|
||||||
|
QString mUrl;
|
||||||
|
|
||||||
|
// Authorization
|
||||||
|
QString mUserName;
|
||||||
|
QString mPassword;
|
||||||
|
QString mToken;
|
||||||
|
|
||||||
|
// Data
|
||||||
|
QByteArray mRequestData;
|
||||||
|
QByteArray mResponseData;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // core
|
||||||
95
src/core/requestmanager.cpp
Normal file
95
src/core/requestmanager.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include "requestmanager.h"
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#ifdef CORESDK_ZLIB
|
||||||
|
#include "compression.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wrapper around QNetworkAccessManger to handle soap protocol
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
RequestManager::RequestManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
connect(&mNetworkAccessManager, &QNetworkAccessManager::authenticationRequired, [](){
|
||||||
|
qCWarning(sdknetwork) << "server need authentication";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Soap::downloadRequest
|
||||||
|
* @param soapRequest
|
||||||
|
*/
|
||||||
|
void RequestManager::downloadRequest(const std::shared_ptr<Request>& request)
|
||||||
|
{
|
||||||
|
QNetworkReply *reply = mNetworkAccessManager.post(request->networkRequest(), request->requestData());
|
||||||
|
mRequestMap.insert(reply, request);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, this, &RequestManager::sslErrors);
|
||||||
|
connect(reply, &QNetworkReply::downloadProgress, this, &RequestManager::downloadProgress);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [=]() {
|
||||||
|
std::shared_ptr<Request> request = mRequestMap.value(reply);
|
||||||
|
mRequestMap.remove(reply);
|
||||||
|
reply->deleteLater();
|
||||||
|
emit request->finished();
|
||||||
|
emit downloadFinished(request);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle sslErrors -> only debug output
|
||||||
|
* @param sslErrors ( from QNetworkReply::sslErrors )
|
||||||
|
*/
|
||||||
|
void RequestManager::sslErrors(const QList<QSslError> &sslErrors)
|
||||||
|
{
|
||||||
|
for (const QSslError &error : sslErrors) {
|
||||||
|
qCDebug(sdknetwork) << QStringLiteral("SSL error:") << error.errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief called upon download progess -> adding data into QByteArray
|
||||||
|
* @param bytesReceived ( can be different from actual data -> compression )
|
||||||
|
* @param bytesTotal ( can be different from actual data -> compression )
|
||||||
|
*/
|
||||||
|
void RequestManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||||
|
{
|
||||||
|
// Get Reply ( now only one )
|
||||||
|
//Q_ASSERT(mCurrentReply);
|
||||||
|
auto *reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
Q_ASSERT(mRequestMap.contains(reply));
|
||||||
|
std::shared_ptr<Request> request = mRequestMap.value(reply);
|
||||||
|
//Q_ASSERT(reply == mCurrentReply);
|
||||||
|
|
||||||
|
QString encoding;
|
||||||
|
|
||||||
|
// Check encoding
|
||||||
|
for (const auto & pair : reply->rawHeaderPairs()) {
|
||||||
|
if (pair.first == QStringLiteral("Content-Encoding")) {
|
||||||
|
encoding = pair.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data = reply->readAll();
|
||||||
|
|
||||||
|
// Decompress gzip data
|
||||||
|
#ifdef CORESDK_ZLIB
|
||||||
|
if (encoding.toLower() == "gzip" && !data.isEmpty() ) {
|
||||||
|
data = Compression::decompress(data);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Append data to request
|
||||||
|
request->appendData(data);
|
||||||
|
|
||||||
|
emit progresss(bytesReceived, bytesTotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // core
|
||||||
42
src/core/requestmanager.h
Normal file
42
src/core/requestmanager.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Will download only ONE ITEM at a time
|
||||||
|
*/
|
||||||
|
class RequestManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT;
|
||||||
|
friend class SoapTest;
|
||||||
|
public:
|
||||||
|
explicit RequestManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void downloadRequest(const std::shared_ptr<Request>& request);
|
||||||
|
|
||||||
|
// QByteArray data() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_SLOT void sslErrors(const QList<QSslError> &sslErrors);
|
||||||
|
Q_SLOT void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void downloadFinished(std::shared_ptr<Request> request);
|
||||||
|
void progresss(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager mNetworkAccessManager;
|
||||||
|
//QNetworkReply *mCurrentReply = nullptr;
|
||||||
|
// QByteArray mData;
|
||||||
|
QMap<QNetworkReply*, std::shared_ptr<Request>> mRequestMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // core
|
||||||
8
src/core/zlib.pri
Normal file
8
src/core/zlib.pri
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/compression.cpp \
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/compression.h \
|
||||||
5
src/lib/lib.pro
Normal file
5
src/lib/lib.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
|
||||||
|
QT += core sql network
|
||||||
|
|
||||||
|
include(../core/core.pri)
|
||||||
6
src/testapp/app.pro
Normal file
6
src/testapp/app.pro
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
|
||||||
|
QT += core sql network
|
||||||
|
CONFIG += debug console
|
||||||
|
|
||||||
|
include(../core/core.pri)
|
||||||
20
src/testapp/main.cpp
Normal file
20
src/testapp/main.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QCoreApplication::setApplicationName("colnod-connector");
|
||||||
|
QCoreApplication::setApplicationVersion("1.0");
|
||||||
|
|
||||||
|
// Command line parsing
|
||||||
|
QCommandLineParser parser;
|
||||||
|
|
||||||
|
parser.setApplicationDescription("Colnod sync");
|
||||||
|
QCommandLineOption dryModeOption("dryMode", "ColnodAPI", "Read-only mode, prints changes which would normally be made");
|
||||||
|
parser.addOption(dryModeOption);
|
||||||
|
parser.process(app);
|
||||||
|
|
||||||
|
// Synchronizaiton
|
||||||
|
return QCoreApplication::exec();
|
||||||
|
}
|
||||||
7
src/testapp/testapp.pro
Normal file
7
src/testapp/testapp.pro
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
QT += core sql network
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
||||||
|
include(../core/core.pri)
|
||||||
5
test/test.pro
Normal file
5
test/test.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS += \
|
||||||
|
unittests
|
||||||
|
|
||||||
19
test/unittests/tst_request.cpp
Normal file
19
test/unittests/tst_request.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "tst_request.h"
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
void RequestTest::auth_test()
|
||||||
|
{
|
||||||
|
QString expected = "Basic VGVzdGVyOlRlc3Q=";
|
||||||
|
Request req;
|
||||||
|
req.setUserName("Tester");
|
||||||
|
req.setPassword("Test");
|
||||||
|
|
||||||
|
QCOMPARE(req.basicAuthorization(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(core::RequestTest)
|
||||||
21
test/unittests/tst_request.h
Normal file
21
test/unittests/tst_request.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class RequestTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
// Default testcases ( not needed to declare it )
|
||||||
|
// Q_SLOT void initTestCase();
|
||||||
|
// Q_SLOT void cleanupTestCase();
|
||||||
|
// void wont_run(); // no Q_SLOT ( only slots will be runned within tests )
|
||||||
|
|
||||||
|
|
||||||
|
Q_SLOT void auth_test();
|
||||||
|
};
|
||||||
|
|
||||||
|
} //
|
||||||
14
test/unittests/unittests.pro
Normal file
14
test/unittests/unittests.pro
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
QT += core sql network testlib
|
||||||
|
CONFIG += testcase # neccesary to have posibility to run: make check UnitTest
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
tst_request.cpp
|
||||||
|
HEADERS += \
|
||||||
|
tst_request.h
|
||||||
|
|
||||||
|
include(../../src/core/core.pri)
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
Loading…
x
Reference in New Issue
Block a user