mirror of
https://gitlab.com/spaceti-app/integrations/common/server-sdk.git
synced 2026-07-12 17:20:45 +02:00
Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a2df4661e | ||
|
|
b1897b6a74 | ||
|
|
b54f2b332e | ||
|
|
396c046afc | ||
|
|
aff09b92da | ||
|
|
fdfe31e064 | ||
|
|
d4c4d75b49 | ||
|
|
2b12a67f52 | ||
|
|
46829870f0 | ||
|
|
a555fd93e2 | ||
|
|
19f748cc73 | ||
|
|
28a61d7c12 | ||
|
|
3640c0db5d | ||
|
|
9088d93a4e | ||
|
|
b643f25421 | ||
|
|
90541c3421 | ||
|
|
031b6c2535 | ||
|
|
e4673cf66a | ||
|
|
ec406abc6f | ||
|
|
a818055b75 | ||
|
|
dbe8aa5775 | ||
|
|
744bc7a785 | ||
|
|
924cfbde0f | ||
|
|
2041fed9fa | ||
|
|
5ee82764e7 | ||
|
|
61feac4632 | ||
|
|
ee08677641 | ||
|
|
2e574f7b49 | ||
|
|
9623578997 | ||
|
|
99089d1b12 | ||
|
|
eddfc401b0 | ||
|
|
9fc60947eb | ||
|
|
38a7da300d | ||
|
|
5025a68891 | ||
|
|
9dd13d2970 | ||
|
|
f387b600f7 | ||
|
|
c038568124 | ||
|
|
7f369b075d | ||
|
|
1cf415820f | ||
|
|
956f51e3e2 | ||
|
|
9cc150d50b | ||
|
|
e266b8a8b4 | ||
|
|
583a652cff | ||
|
|
7071d874b0 | ||
|
|
793bfde1de | ||
|
|
4dfd4227cb | ||
|
|
34d112062c | ||
|
|
ad68c85086 | ||
|
|
8821dfc9b2 | ||
|
|
29f7103ece | ||
|
|
22d45162d4 | ||
|
|
5ea2b6f3e1 | ||
|
|
886cc3c777 | ||
|
|
dfe6d3bb03 | ||
|
|
f1d78192f7 | ||
|
|
6940cad69e | ||
|
|
3f3af6cfa6 | ||
|
|
549ba2abe2 | ||
|
|
4983bbb782 | ||
|
|
b92095a459 | ||
|
|
16fe2db973 | ||
|
|
c4c51e5099 | ||
|
|
136ee8300a | ||
|
|
493d82896b | ||
|
|
eb2a6c92fa | ||
|
|
174ede92ee | ||
|
|
30925777b4 | ||
|
|
a80552654b | ||
|
|
a7831cae9c | ||
|
|
08f7f163d2 | ||
|
|
d467feb7b9 | ||
|
|
995898c00c | ||
|
|
1cd286dba5 | ||
|
|
d98ddf6c5f | ||
|
|
b04332059f | ||
|
|
70e389ab43 | ||
|
|
d54da50d5e | ||
|
|
54d7a5c59a | ||
|
|
1fc722b751 | ||
|
|
8552cc304f | ||
|
|
1d947e931b | ||
|
|
33950c96fe | ||
|
|
ee507683c6 | ||
|
|
4ded480088 | ||
|
|
22e281a2a7 | ||
|
|
00596d53db | ||
|
|
8f2fcedeb7 | ||
|
|
5618a7d00c | ||
|
|
ce9e59d604 | ||
|
|
01bd3c9753 | ||
|
|
0d4f436e23 | ||
|
|
1fe9632720 | ||
|
|
8c137c391f | ||
|
|
0e2922f673 | ||
|
|
1c2d69786c | ||
|
|
aeb98c0f06 | ||
|
|
ea0258cef1 | ||
|
|
ea042620b1 | ||
|
|
b6950b9bfe | ||
|
|
57e0245b1b | ||
|
|
27a533d52b | ||
|
|
c24bfa5296 | ||
|
|
02caeb86c9 | ||
|
|
0cf21cf7c2 | ||
|
|
cb61286f33 | ||
|
|
e650163631 | ||
|
|
6909ae182c | ||
|
|
0c7887d944 |
@ -2,9 +2,17 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
- quality
|
|
||||||
|
|
||||||
# ########
|
# To clone also submodules
|
||||||
|
variables:
|
||||||
|
PRO_FILE: ../server-sdk.pro
|
||||||
|
SCRIPT_PATH: scripts
|
||||||
|
|
||||||
|
#cache:
|
||||||
|
# paths:
|
||||||
|
# - build/
|
||||||
|
|
||||||
|
##########
|
||||||
# Build
|
# Build
|
||||||
##########
|
##########
|
||||||
build: # name of task
|
build: # name of task
|
||||||
@ -15,14 +23,11 @@ build: # name of task
|
|||||||
- if [ -d build ]; then rm -rf build; fi
|
- if [ -d build ]; then rm -rf build; fi
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- qmake ../server-sdk.pro
|
- qmake $PRO_FILE
|
||||||
- make -j4
|
- make -j4
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- build/
|
|
||||||
|
|
||||||
|
|
||||||
# ########
|
##########
|
||||||
# Test
|
# Test
|
||||||
##########
|
##########
|
||||||
test:
|
test:
|
||||||
@ -30,39 +35,56 @@ test:
|
|||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
script:
|
script:
|
||||||
|
- if [ -d build ]; then rm -rf build; fi
|
||||||
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- make check test
|
- qmake CONFIG+=debug $PRO_FILE
|
||||||
|
- make check
|
||||||
|
|
||||||
|
# memory leak check for unittest
|
||||||
valgrind:
|
valgrind:
|
||||||
stage: test
|
stage: test
|
||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
script:
|
script:
|
||||||
|
- if [ -d build ]; then rm -rf build; fi
|
||||||
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- valgrind make check test
|
- qmake $PRO_FILE
|
||||||
|
- valgrind make check
|
||||||
|
|
||||||
# Quality of code
|
# Quality of code
|
||||||
cppcheck:
|
cppcheck:
|
||||||
stage: test
|
stage: test
|
||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
# Probably needed to allow failure
|
#allow_failure: true # Probably needed to allow failure
|
||||||
#allow_failure: true
|
script:
|
||||||
script: # run in bash
|
- $SCRIPT_PATH/cppcheck.sh
|
||||||
- scripts/cppcheck.sh
|
dependencies: []
|
||||||
|
|
||||||
|
# Common checks ( Line endings )
|
||||||
|
lineendings:
|
||||||
|
stage: test
|
||||||
|
tags:
|
||||||
|
- shell
|
||||||
|
script:
|
||||||
|
- $SCRIPT_PATH/check.sh
|
||||||
|
dependencies: []
|
||||||
|
|
||||||
codecoverage:
|
codecoverage:
|
||||||
stage: test
|
stage: test
|
||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
script:
|
script:
|
||||||
- scripts/codecoverage.sh
|
- $SCRIPT_PATH/codecoverage.sh
|
||||||
coverage: '/^\s+functions..:\s+(\d+\.\d+%).+$/'
|
coverage: '/^\s+functions..:\s+(\d+\.\d+%).+$/'
|
||||||
|
dependencies: []
|
||||||
|
|
||||||
doc:
|
doc:
|
||||||
stage: test
|
stage: test
|
||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
script:
|
script:
|
||||||
- scripts/doc.sh clean # 'clean' better clean before generating documentation
|
- $SCRIPT_PATH/doc.sh clean # 'clean' better clean before generating documentation
|
||||||
|
dependencies: []
|
||||||
|
|||||||
7
ansible/ansible.cfg
Normal file
7
ansible/ansible.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[defaults]
|
||||||
|
|
||||||
|
# problem with 'create_db' 'Ensure database is created'
|
||||||
|
# @see https://github.com/georchestra/ansible/issues/55
|
||||||
|
# Have to make temorary files readable by all, beacuse when chaning user
|
||||||
|
# -> that use does not have access to temp file ( run scripts )
|
||||||
|
allow_world_readable_tmpfiles=true
|
||||||
7
ansible/ansible.md
Normal file
7
ansible/ansible.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Ansible
|
||||||
|
|
||||||
|
Ansible file taken from: server-tools
|
||||||
|
|
||||||
|
This will instlal `serversdk` test database on `spaceti-experimental`
|
||||||
|
|
||||||
|
`ansible/playbook.sh install-postgres`
|
||||||
8
ansible/install-postgres.yml
Normal file
8
ansible/install-postgres.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
- hosts: db
|
||||||
|
become: yes
|
||||||
|
vars_files:
|
||||||
|
- postgresql/vars.yml
|
||||||
|
vars:
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- geerlingguy.postgresql
|
||||||
10
ansible/inventory.yml
Normal file
10
ansible/inventory.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
db:
|
||||||
|
hosts:
|
||||||
|
spaceti-experimental:
|
||||||
|
ansible_host: 192.168.1.94
|
||||||
|
server_name: 'spaceti-experimental'
|
||||||
|
ansible_user: 'filip'
|
||||||
|
ansible_private_key_file: '~/.ssh/spaceti_gitlab'
|
||||||
|
# ansible_python_interpreter: '/usr/bin/python'
|
||||||
6
ansible/playbook.sh
Executable file
6
ansible/playbook.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
|
||||||
|
cd $SRCDIR
|
||||||
|
ansible-playbook -i inventory.yml $1.yml
|
||||||
19
ansible/postgresql/vars.yml
Normal file
19
ansible/postgresql/vars.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
no_log: no
|
||||||
|
postgresql_databases:
|
||||||
|
- name: serversdk
|
||||||
|
postgresql_users:
|
||||||
|
- name: postgres
|
||||||
|
password: 34rjkciea12
|
||||||
|
postgresql_group: postgres
|
||||||
|
postgresql_global_config_options:
|
||||||
|
- option: listen_addresses
|
||||||
|
value: '*'
|
||||||
|
postgresql_hba_entries:
|
||||||
|
- { type: local, database: all, user: postgres, auth_method: peer }
|
||||||
|
- { type: local, database: all, user: all, auth_method: peer }
|
||||||
|
- { type: host, database: all, user: all, address: '127.0.0.1/32', auth_method: md5 }
|
||||||
|
- { type: host, database: all, user: all, address: '::1/128', auth_method: md5 }
|
||||||
|
- { type: host, database: all, user: all, address: '0.0.0.0/0', auth_method: md5 } # full access
|
||||||
|
# - { type: host, database: all, user: all, address: '178.209.130.196/32', auth_method: md5 } # access from specific IP only
|
||||||
|
postgresql_restarted_state: "restarted"
|
||||||
30
readme.md
30
readme.md
@ -2,3 +2,33 @@
|
|||||||
|
|
||||||
[](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master)
|
[](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master)
|
||||||
[](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master)
|
[](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master)
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### How to use predefined classes
|
||||||
|
|
||||||
|
#### DatabaseTable, RequestMangager
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class LoginTable : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, username, set_username, "");
|
||||||
|
TABLE_PROPERTY(QString, password, set_password, "")
|
||||||
|
};
|
||||||
|
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
reqMan.setIgnoreSsl(true);
|
||||||
|
|
||||||
|
auto loginRequest = std::make_shared<serversdk::Request>("POST", "https://192.168.1.3:8443/AaaManager/authSession", login->jsonData());
|
||||||
|
|
||||||
|
// Set basic authorization
|
||||||
|
loginRequest->setUserName(login->username());
|
||||||
|
loginRequest->setPassword(login->password());
|
||||||
|
|
||||||
|
reqMan.processRequest(loginRequest);
|
||||||
|
|
||||||
|
connect(loginRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
qDebug() << "Request is finished";
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
20
scripts/check.sh
Executable file
20
scripts/check.sh
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
source $CURRENT_DIR/common.sh
|
||||||
|
|
||||||
|
SOURCE_DIR=$PWD
|
||||||
|
|
||||||
|
cd $SOURCE_DIR
|
||||||
|
|
||||||
|
|
||||||
|
info "Checking line ending"
|
||||||
|
|
||||||
|
RESULT=$( find . -not -type d ! -path '*.git/*' -exec file "{}" ";" | grep "ASCII text, with CRLF" | cat )
|
||||||
|
|
||||||
|
if [ ! -z "$RESULT" ]; then
|
||||||
|
echo "----------------------------------"
|
||||||
|
echo "$RESULT"
|
||||||
|
echo "----------------------------------"
|
||||||
|
fail "LF check finished with error"
|
||||||
|
fi
|
||||||
@ -15,8 +15,8 @@ PRO_FILE=$(ls *.pro)
|
|||||||
|
|
||||||
# Ensuring server-sdk wont be filtered out for server-sdk
|
# Ensuring server-sdk wont be filtered out for server-sdk
|
||||||
NAME=$( basename $PWD )
|
NAME=$( basename $PWD )
|
||||||
if [ "$NAME" != "server-sdk" ]; then
|
if [ "$NAME" != "serversdk" ]; then
|
||||||
COVERAGE_FILTER+=" *server-sdk*"
|
COVERAGE_FILTER+=" *serversdk*"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensuring 'src' dir exists
|
# Ensuring 'src' dir exists
|
||||||
@ -35,6 +35,7 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if [ -d $BUILDPATH ]; then info "Removing build path: $BUILDPATH"; rm -rf $BUILDPATH; fi
|
||||||
info "Creating: $BUILDPATH"
|
info "Creating: $BUILDPATH"
|
||||||
mkdir -p $BUILDPATH
|
mkdir -p $BUILDPATH
|
||||||
cd $BUILDPATH
|
cd $BUILDPATH
|
||||||
@ -49,7 +50,7 @@ info "Build"
|
|||||||
make -j4 # build
|
make -j4 # build
|
||||||
|
|
||||||
info "Run unittests"
|
info "Run unittests"
|
||||||
make check test
|
make check
|
||||||
|
|
||||||
|
|
||||||
echo "--------------------"
|
echo "--------------------"
|
||||||
|
|||||||
0
scripts/common.sh
Normal file → Executable file
0
scripts/common.sh
Normal file → Executable file
@ -7,8 +7,13 @@ SOURCE_DIR=$PWD
|
|||||||
|
|
||||||
EXCLUDE_DIR=build
|
EXCLUDE_DIR=build
|
||||||
CHECK_DIR=$SOURCE_DIR/src
|
CHECK_DIR=$SOURCE_DIR/src
|
||||||
SUPPRESS="--suppress=missingInclude --suppress=unusedFunction"
|
SUPPRESS="--suppress=missingInclude"
|
||||||
|
|
||||||
|
# Ensuring server-sdk will supress 'unuseFunction' warning
|
||||||
|
NAME=$( basename $PWD )
|
||||||
|
if [ "$NAME" == "server-sdk" ]; then
|
||||||
|
SUPPRESS=$SUPPRESS" --suppress=unusedFunction"
|
||||||
|
fi
|
||||||
|
|
||||||
info cppcheck version: `cppcheck --version`
|
info cppcheck version: `cppcheck --version`
|
||||||
if [ ! -d src ]; then
|
if [ ! -d src ]; then
|
||||||
|
|||||||
@ -14,7 +14,5 @@ OTHER_FILES += \
|
|||||||
scripts/* \
|
scripts/* \
|
||||||
doc/Doxy* \
|
doc/Doxy* \
|
||||||
doc/pages/* \
|
doc/pages/* \
|
||||||
serversdk.pri \
|
*.pri \
|
||||||
serversdkint.pri \
|
|
||||||
|
|
||||||
message($$OUT_ROOT)
|
|
||||||
|
|||||||
@ -31,9 +31,11 @@ win32:CONFIG(release, debug|release): LIBS += -L$$SDKDIR/release/ -lserversdk
|
|||||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SDKDIR/debug/ -lserversdk
|
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SDKDIR/debug/ -lserversdk
|
||||||
else:unix: LIBS += -L$$SDKDIR/ -lserversdk
|
else:unix: LIBS += -L$$SDKDIR/ -lserversdk
|
||||||
|
|
||||||
|
QT += network sql
|
||||||
|
CONFIG += c++14
|
||||||
|
|
||||||
INCLUDEPATH += $$PWD/src/
|
INCLUDEPATH += $$shell_quote($$PWD/src/)
|
||||||
DEPENDPATH += $$PWD/src/serversdk
|
DEPENDPATH += $$shell_quote($$PWD/src/serversdk)
|
||||||
|
|
||||||
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$SDKDIR/release/libserversdk.a
|
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$SDKDIR/release/libserversdk.a
|
||||||
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$SDKDIR/debug/libserversdk.a
|
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$SDKDIR/debug/libserversdk.a
|
||||||
|
|||||||
@ -23,11 +23,15 @@ SDKDIR=$$ROOT_OUT/src/serversdk
|
|||||||
|
|
||||||
win32:CONFIG(release, debug|release): LIBS += -L$$SDKDIR/release/ -lserversdk
|
win32:CONFIG(release, debug|release): LIBS += -L$$SDKDIR/release/ -lserversdk
|
||||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SDKDIR/debug/ -lserversdk
|
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SDKDIR/debug/ -lserversdk
|
||||||
else:unix: LIBS += -L$$SDKDIR/ -lserversdk
|
else:unix: LIBS += -L$$SDKDIR/ -lserversdk -lz
|
||||||
|
|
||||||
|
|
||||||
|
QT += network sql
|
||||||
|
CONFIG += c++14
|
||||||
INCLUDEPATH += $$PWD/src/
|
INCLUDEPATH += $$PWD/src/
|
||||||
DEPENDPATH += $$PWD/src/serversdk
|
DEPENDPATH += $$PWD/src/serversdk
|
||||||
|
# enable zlib compression on linux
|
||||||
|
unix: DEFINES += USE_ZLIB
|
||||||
|
|
||||||
|
|
||||||
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$SDKDIR/release/libserversdk.a
|
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$SDKDIR/release/libserversdk.a
|
||||||
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$SDKDIR/debug/libserversdk.a
|
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$SDKDIR/debug/libserversdk.a
|
||||||
|
|||||||
@ -10,7 +10,7 @@ void installLogger(QtMsgType type, const QMessageLogContext &context, const QStr
|
|||||||
void initAppLogger() {
|
void initAppLogger() {
|
||||||
auto res = qInstallMessageHandler(serversdk::Logger::outputMessage);
|
auto res = qInstallMessageHandler(serversdk::Logger::outputMessage);
|
||||||
if (res)
|
if (res)
|
||||||
qCInfo(sdkcore) << "user message handler qInstallMessageHandler installed sucessfully";
|
qCDebug(sdkcore) << "user message handler qInstallMessageHandler installed sucessfully";
|
||||||
else
|
else
|
||||||
qCWarning(sdkcore) << "failed to install message handler qInstallMessageHandler";
|
qCWarning(sdkcore) << "failed to install message handler qInstallMessageHandler";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,39 +1,138 @@
|
|||||||
#include "databasemanager.h"
|
#include "databasemanager.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
DatabaseManager::DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool dryMode)
|
DatabaseManager::DatabaseManager()
|
||||||
: mDryMode(dryMode)
|
|
||||||
{
|
{
|
||||||
db = QSqlDatabase::addDatabase("QPSQL");
|
}
|
||||||
db.setDatabaseName(dbName);
|
|
||||||
db.setHostName(host);
|
|
||||||
db.setUserName(user); // home-office stuff -->> db.setPort(80);
|
|
||||||
db.setPassword(password);
|
DatabaseManager::~DatabaseManager()
|
||||||
if (db.open())
|
|
||||||
{
|
{
|
||||||
query = QSqlQuery("query", db);
|
if (mDatabase.isValid()) {
|
||||||
} else {
|
mDatabase.close();
|
||||||
log("Opening error" + db.lastError().text());
|
mDatabase = QSqlDatabase(); // invalidate database
|
||||||
emit error();
|
qDebug(sdkdb) << "removing database connection:" << mConnectionName;
|
||||||
|
QSqlDatabase::removeDatabase(mConnectionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseManager::executeQuery(QSqlQuery &query)
|
/**
|
||||||
|
* @brief Creates new SQLite connection
|
||||||
|
* @param path to database ( will be created when none )
|
||||||
|
* @param connectionName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool DatabaseManager::openSQLite(const QString &path, const QString &connectionName)
|
||||||
{
|
{
|
||||||
|
mConnectionName = connectionName;
|
||||||
|
mDatabase = QSqlDatabase::addDatabase("QSQLITE", mConnectionName);
|
||||||
|
mDatabase.setDatabaseName(path);
|
||||||
|
|
||||||
|
int res = mDatabase.open();
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
qInfo(sdkdb) << "Not possible to open database from path:" << path;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::openPostgres(const QString &host, const QString &dbName, const QString &user, const QString &password, const QString &connectionName)
|
||||||
|
{
|
||||||
|
Q_ASSERT_X(mConnectionName.isEmpty(), "open postgres", "Expecting only one database per DatabaseManager");
|
||||||
|
mConnectionName = connectionName;
|
||||||
|
mDatabase = QSqlDatabase::addDatabase("QPSQL", mConnectionName);
|
||||||
|
|
||||||
|
mDatabase.setDatabaseName(dbName);
|
||||||
|
mDatabase.setHostName(host);
|
||||||
|
mDatabase.setUserName(user); // home-office stuff -->> db.setPort(80);
|
||||||
|
mDatabase.setPassword(password);
|
||||||
|
// @see https://www.qtcentre.org/threads/39913-QSqlDatabase-connection-timeout
|
||||||
|
mDatabase.setConnectOptions("connect_timeout=10");
|
||||||
|
|
||||||
|
int res = mDatabase.open();
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
qInfo(sdkdb) << "Not possible to open database from url:" << host;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DatabaseManager::isValid() const
|
||||||
|
{
|
||||||
|
return mDatabase.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::isOpen() const
|
||||||
|
{
|
||||||
|
return mDatabase.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::close()
|
||||||
|
{
|
||||||
|
mDatabase.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery DatabaseManager::execPlainSql(const QString &sqlText)
|
||||||
|
{
|
||||||
|
QSqlQuery query(mDatabase);
|
||||||
|
return executeQuery(query, sqlText);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery DatabaseManager::executeQuery(QSqlQuery &query, const QString &sqlText)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Get correct SQL statement and correct isSelect
|
||||||
|
// Need for output correct SQL command
|
||||||
|
// isSelect is needed for correct bahaviour of 'dryMode' only select should be executed
|
||||||
|
QString sqlStatement = sqlText;
|
||||||
|
bool isSelect = false;
|
||||||
|
if ( sqlText.isEmpty()) {
|
||||||
|
sqlStatement = getLastQuery(query);
|
||||||
|
}
|
||||||
|
if (sqlText.startsWith("SELECT", Qt::CaseInsensitive)) {
|
||||||
|
isSelect = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (mDryMode) {
|
if (mDryMode) {
|
||||||
QString sqlStatement = getLastQuery(query);
|
qCInfo(sdkdb) << "SQL statement: " << sqlStatement;
|
||||||
qDebug() << sqlStatement;
|
}
|
||||||
|
|
||||||
|
// Execute only SELECT statement or when not drymode
|
||||||
|
if ( isSelect || !mDryMode ) {
|
||||||
|
int res = false;
|
||||||
|
if ( ! sqlText.isEmpty()) { // prepared statement.
|
||||||
|
res = query.exec(sqlText);
|
||||||
|
return processResult(res, query);
|
||||||
}
|
}
|
||||||
if (!mDryMode || query.isSelect()){
|
res = query.exec();
|
||||||
if (!query.exec()){
|
return processResult(res, query);
|
||||||
QString msg = QString("Source: Database Manager, Error: %1").arg(query.lastError().text());
|
|
||||||
log(QString(msg));
|
|
||||||
emit error();
|
|
||||||
}
|
}
|
||||||
|
return query;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
QSqlQuery DatabaseManager::createQuery()
|
||||||
|
{
|
||||||
|
QSqlQuery query(mDatabase);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery DatabaseManager::processResult(int res, const QSqlQuery &query) const
|
||||||
|
{
|
||||||
|
if ( !res ) {
|
||||||
|
QString msg = QString("Source: Database Manager \nError --------\n%1\nSQL-------\n%2\n--------")
|
||||||
|
.arg(query.lastError().text(), query.lastQuery());
|
||||||
|
qCDebug(sdkdb).noquote() << msg;
|
||||||
|
emit sqlError(query);
|
||||||
|
log(msg);
|
||||||
|
}
|
||||||
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DatabaseManager::getLastQuery(const QSqlQuery& query) const
|
QString DatabaseManager::getLastQuery(const QSqlQuery& query) const
|
||||||
@ -51,6 +150,23 @@ QString DatabaseManager::getLastQuery(const QSqlQuery& query) const
|
|||||||
void DatabaseManager::log(const QString &msg) const
|
void DatabaseManager::log(const QString &msg) const
|
||||||
{
|
{
|
||||||
qDebug() << "database logger implement logger";
|
qDebug() << "database logger implement logger";
|
||||||
|
qDebug() << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::dryMode() const
|
||||||
|
{
|
||||||
|
return mDryMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::setDryMode(bool dryMode)
|
||||||
|
{
|
||||||
|
mDryMode = dryMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase DatabaseManager::database() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(mDatabase.isValid());
|
||||||
|
return mDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // core
|
} // core
|
||||||
|
|||||||
@ -17,19 +17,39 @@ class DatabaseManager : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
friend class Logger;
|
friend class Logger;
|
||||||
public:
|
public:
|
||||||
|
DatabaseManager();
|
||||||
DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool mDryMode = false);
|
DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool mDryMode = false);
|
||||||
|
~DatabaseManager() override;
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
bool dryMode() const;
|
||||||
|
void setDryMode(bool dryMode);
|
||||||
|
|
||||||
|
bool openSQLite(const QString &path, const QString &connectionName = QStringLiteral("qt_sql_default_connection"));
|
||||||
|
bool openPostgres(const QString& host, const QString& dbName, const QString& user, const QString& password, const QString &connectionName = QStringLiteral("qt_sql_default_connection"));
|
||||||
|
bool isValid() const;
|
||||||
|
bool isOpen() const;
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// SQL handling
|
||||||
|
QSqlQuery execPlainSql(const QString &sqlText);
|
||||||
|
QSqlQuery executeQuery(QSqlQuery &query, const QString &sqlText = QString());
|
||||||
|
QSqlQuery createQuery();
|
||||||
|
QSqlDatabase database() const;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool executeQuery(QSqlQuery &query);
|
QSqlQuery processResult(int res, const QSqlQuery &query) const;
|
||||||
QString getLastQuery(const QSqlQuery &query) const;
|
QString getLastQuery(const QSqlQuery &query) const;
|
||||||
|
|
||||||
void log(const QString &msg) const;
|
void log(const QString &msg) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void error();
|
void sqlError(const QSqlQuery &query) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSqlDatabase db;
|
QString mConnectionName;
|
||||||
|
QSqlDatabase mDatabase;
|
||||||
QSqlQuery query;
|
QSqlQuery query;
|
||||||
bool mDryMode = false;
|
bool mDryMode = false;
|
||||||
};
|
};
|
||||||
|
|||||||
157
src/serversdk/databasetable.cpp
Normal file
157
src/serversdk/databasetable.cpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#include "databasetable.h"
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QMetaProperty>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
DatabaseTable::DatabaseTable(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseTable::primaryKey() const
|
||||||
|
{
|
||||||
|
const QMetaObject *metaObj = this->metaObject();
|
||||||
|
for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
|
||||||
|
QMetaProperty prop = metaObj->property(i);
|
||||||
|
if (!prop.isUser())
|
||||||
|
continue;
|
||||||
|
return prop.name();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray DatabaseTable::jsonData(const QSet<QString> &filters) const
|
||||||
|
{
|
||||||
|
auto variantMap = this->toVariantMap(filters);
|
||||||
|
QJsonDocument doc = QJsonDocument::fromVariant(variantMap);
|
||||||
|
return doc.toJson(QJsonDocument::Compact);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseTable::fromJson(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// in: JSON ( e.g. {"id": 300} )
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
QVariantMap variant = qvariant_cast<QVariantMap>(doc.toVariant());
|
||||||
|
|
||||||
|
// Populate this object data
|
||||||
|
bool res = fromVariantMap(variant);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates universal variant structure to hold key / value pairs */
|
||||||
|
QVariantMap DatabaseTable::toVariantMap(const QSet<QString> &filters) const
|
||||||
|
{
|
||||||
|
QVariantMap variantMap;
|
||||||
|
|
||||||
|
const QMetaObject *metaObj = this->metaObject();
|
||||||
|
for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
|
||||||
|
QMetaProperty prop = metaObj->property(i);
|
||||||
|
// Skip non USER so -> ( Q_PROPERTY( ...... USER false ) only
|
||||||
|
if (!prop.isUser())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get key -> value pairs
|
||||||
|
QString name = prop.name();
|
||||||
|
|
||||||
|
// Filter property
|
||||||
|
if (filters.contains(name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QVariant::Type type = prop.type();
|
||||||
|
QVariant value(type);
|
||||||
|
value.setValue(prop.read(this));
|
||||||
|
|
||||||
|
variantMap.insert(name, value);
|
||||||
|
}
|
||||||
|
return variantMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DatabaseTable::fromVariantMap(const QVariantMap &variantMap)
|
||||||
|
{
|
||||||
|
|
||||||
|
const QMetaObject *metaObj = this->metaObject();
|
||||||
|
for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
|
||||||
|
QMetaProperty prop = metaObj->property(i);
|
||||||
|
// Skip non USER so -> ( Q_PROPERTY( ...... USER false ) only
|
||||||
|
if (!prop.isUser())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Property name
|
||||||
|
QString name = prop.name();
|
||||||
|
// Get value from variant map
|
||||||
|
|
||||||
|
QVariant value = variantMap.value(name);
|
||||||
|
prop.write(this, value);
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QList<std::shared_ptr<DatabaseTable> > DatabaseTable::fromListVariantMap(const QList<QVariantMap> &listMap)
|
||||||
|
{
|
||||||
|
QList<std::shared_ptr<DatabaseTable>> list;
|
||||||
|
for (const QVariantMap &map : listMap) {
|
||||||
|
auto data = std::make_shared<DatabaseTable>();
|
||||||
|
data->fromVariantMap(map);
|
||||||
|
list << data;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString DatabaseTable::textSQLIteType(QVariant::Type type) const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QVariant::Int:
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
break;
|
||||||
|
case QVariant::Uuid:
|
||||||
|
return QStringLiteral("TEXT");
|
||||||
|
break;
|
||||||
|
case QVariant::String:
|
||||||
|
return QStringLiteral("TEXT");
|
||||||
|
break;
|
||||||
|
case QVariant::DateTime:
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
break;
|
||||||
|
case QVariant::Bool:
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return QString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseTable::textPostgresType(QVariant::Type type) const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QVariant::Int:
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
break;
|
||||||
|
case QVariant::Uuid:
|
||||||
|
return QStringLiteral("TEXT");
|
||||||
|
break;
|
||||||
|
case QVariant::String:
|
||||||
|
return QStringLiteral("TEXT");
|
||||||
|
break;
|
||||||
|
case QVariant::DateTime:
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
break;
|
||||||
|
case QVariant::Bool:
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return QString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace serversdk
|
||||||
166
src/serversdk/databasetable.h
Normal file
166
src/serversdk/databasetable.h
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariantMap>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
#define TABLE_PROPERTY(TYPE, NAME, SETNAME, INITVAL) \
|
||||||
|
Q_PROPERTY(TYPE NAME READ NAME WRITE SETNAME NOTIFY NAME ## Changed USER true) \
|
||||||
|
public: \
|
||||||
|
TYPE NAME() const { return a_ ## NAME ; } \
|
||||||
|
Q_SLOT void SETNAME(const TYPE &value) { \
|
||||||
|
if (a_ ## NAME == value) return; \
|
||||||
|
a_ ## NAME = value; \
|
||||||
|
emit NAME ## Changed(value); \
|
||||||
|
} \
|
||||||
|
Q_SIGNAL void NAME ## Changed(TYPE value);\
|
||||||
|
private: \
|
||||||
|
TYPE a_ ## NAME = INITVAL;
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
// Default TABLE_PROPERTY
|
||||||
|
/** # DatabaseTable
|
||||||
|
*
|
||||||
|
* Class for automatic CREATE/INSERT/SELECT and JSON handling
|
||||||
|
*
|
||||||
|
* ## Usage
|
||||||
|
*
|
||||||
|
* 1. define custom class by subclassint serversdk::DatabaseTable
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
class Names : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(int, id, set_id, 0);
|
||||||
|
TABLE_PROPERTY(QString, name, set_name, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Prepare temporary path for database
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QString dbpath = dir.path() + "/testdb.sqlite";
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openSQLite(dbpath);
|
||||||
|
|
||||||
|
|
||||||
|
Names names;
|
||||||
|
|
||||||
|
// CREATE TABLE
|
||||||
|
serversdk::DatabaseUtils utils(&db);
|
||||||
|
QSqlQuery query = utils.createTable(&names, "names");
|
||||||
|
|
||||||
|
// INSERT INTO TABLE
|
||||||
|
names.set_id(2);
|
||||||
|
names.set_name("Testname");
|
||||||
|
|
||||||
|
utils.insertIntoTable(&names, "names");
|
||||||
|
|
||||||
|
// SELECT FROM TABLE
|
||||||
|
QSqlQuery selectSQL = db.execPlainSql("SELECT * FROM names");
|
||||||
|
auto list = utils.tableFromQuery<Names>(selectSQL);
|
||||||
|
}
|
||||||
|
*
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* ## Warning
|
||||||
|
*
|
||||||
|
* Derivation of derived class from DatabaseTable does not work ( missing properties )
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* in: TABLE_PROPERTY
|
||||||
|
* Q_PROPERTY
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DatabaseTable : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DatabaseTable(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString primaryKey() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates JSON from QMetaObject data ( Q_PROPERTY ) -> TABLE_PROPERTY
|
||||||
|
* @return text json e.g. { "id": 00344 }
|
||||||
|
*/
|
||||||
|
virtual QByteArray jsonData(const QSet<QString> &filters = {}) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Populates object with JSON {"id": 300} )
|
||||||
|
* @return true if success TODO: not implemented corrrectly
|
||||||
|
*/
|
||||||
|
virtual bool fromJson(const QByteArray &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates VariantMap from QMetaObject data ( Q_PROPERTY )
|
||||||
|
* @return QVariantMap containing property with key/value pairs
|
||||||
|
*/
|
||||||
|
virtual QVariantMap toVariantMap(const QSet<QString> &filters = {}) const;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Populates object with QVariantMap
|
||||||
|
* @return true if success TODO: not implemented corrrectly
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool fromVariantMap(const QVariantMap &variantMap);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates QList of objects from SELECT query
|
||||||
|
* @param rows with QVariantMap ( rows of key/value pairs )
|
||||||
|
* @return List of objects
|
||||||
|
*/
|
||||||
|
static QList<std::shared_ptr<DatabaseTable>> fromListVariantMap(const QList<QVariantMap> &listMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert QVariant type to SQL type
|
||||||
|
* @param QVariant type ( QString, int, QDateTime )
|
||||||
|
* @return text form of database type ( INTEGER, TEXT )
|
||||||
|
*/
|
||||||
|
virtual QString textSQLIteType(QVariant::Type type) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert QVariant type to SQL type
|
||||||
|
* @param QVariant type ( QString, int, QDateTime )
|
||||||
|
* @return text form of database type ( INTEGER, TEXT )
|
||||||
|
*
|
||||||
|
* # Sublassing
|
||||||
|
*
|
||||||
|
* When it is neccessary to use different type it is possible to subclass and override this method.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
class BackendTable : public serversdk::DatabaseTable
|
||||||
|
{
|
||||||
|
QString textPostgresType(QVariant::Type type) const override;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendTable::textPostgresType(QVariant::Type type) const override
|
||||||
|
{
|
||||||
|
// For case when saving date time as ms since epoch
|
||||||
|
if (type == QVariant::DateTime) {
|
||||||
|
return QStringLiteral("INTEGER");
|
||||||
|
}
|
||||||
|
return DatabaseTable::textPostgresType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual QString textPostgresType(QVariant::Type type) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace serversdk
|
||||||
|
|
||||||
229
src/serversdk/databaseutils.cpp
Normal file
229
src/serversdk/databaseutils.cpp
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
#include "databaseutils.h"
|
||||||
|
#include "databasemanager.h"
|
||||||
|
#include "databasetable.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <QMetaProperty>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DatabaseUtils::createTableSQL
|
||||||
|
* @param table
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
* @code{sql}
|
||||||
|
* "CREATE TABLE colsunod_bject ("
|
||||||
|
"id character(36) primary key NOT NULL,"
|
||||||
|
"description character varying(255),"
|
||||||
|
"last_update timestamp NOT NULL,"
|
||||||
|
"name character varying(255),"
|
||||||
|
"pin character varying(255),"
|
||||||
|
"last_modified timestamp,"
|
||||||
|
"deleted BOOLEAN)"
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Default types:
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
DatabaseUtils::DatabaseUtils(DatabaseManager *manager, QString type)
|
||||||
|
: mDatabaseManager(manager), mSqlType(std::move(type))
|
||||||
|
{
|
||||||
|
Q_ASSERT(mDatabaseManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString DatabaseUtils::createTableText(const DatabaseTable *table, const QString &table_name) const
|
||||||
|
{
|
||||||
|
QString userData;
|
||||||
|
|
||||||
|
if (table_name.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// out: "CREATE TABLE colnod_subject ( "
|
||||||
|
QString sql = QString("CREATE TABLE %1 ( ").arg(table_name);
|
||||||
|
|
||||||
|
const QMetaObject *metaObj = table->metaObject();
|
||||||
|
int total = metaObj->propertyCount();
|
||||||
|
int first = true;
|
||||||
|
for (int i = metaObj->propertyOffset(); i < total; ++i) {
|
||||||
|
QMetaProperty prop = metaObj->property(i);
|
||||||
|
// Skip non USER so -> ( Q_PROPERTY( ...... USER false ) only
|
||||||
|
if (!prop.isUser())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QString name = fromCamelCase(prop.name());
|
||||||
|
QVariant::Type type = prop.type();
|
||||||
|
QString textType;
|
||||||
|
|
||||||
|
|
||||||
|
// Getting correct type based on QVariant
|
||||||
|
if (sqlType() == "SQLITE") {
|
||||||
|
textType = table->textSQLIteType(type);
|
||||||
|
} else if (sqlType() == "PSQL") {
|
||||||
|
textType = table->textSQLIteType(type);
|
||||||
|
//return QString();
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkdb) << "Implemented only for SQLITE";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// in: id TEXT
|
||||||
|
// out: "CREATE TABLE colnod_subject ( id TEXT "
|
||||||
|
sql += QString("%1 %2").arg(name,textType);
|
||||||
|
|
||||||
|
// in: PRIMARY KEY
|
||||||
|
// out: "CREATE TABLE colnod_subject ( id TEXT PRIMARY KEY"
|
||||||
|
if (first) { // first row
|
||||||
|
sql += " PRIMARY KEY NOT NULL";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in: NOT NULL
|
||||||
|
// out: "CREATE TABLE colnod_subject ( id TEXT PRIMARY KEY NOT NULL"
|
||||||
|
if (!userData.isEmpty()) {
|
||||||
|
sql += " " + userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// out: "CREATE TABLE colnod_subject ( id TEXT PRIMARY KEY NOT NULL,"
|
||||||
|
if (i < (total-1)) { // second last
|
||||||
|
sql += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// out: ".. subject_id TEXT)"
|
||||||
|
sql+= " )";
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery DatabaseUtils::createTable(const DatabaseTable *table, const QString &table_name) const
|
||||||
|
{
|
||||||
|
// CREATE TABLE ...
|
||||||
|
QString sql = createTableText(table, table_name);
|
||||||
|
qCDebug(sdkdb) << "SQL:" << sql;
|
||||||
|
return mDatabaseManager->execPlainSql(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DatabaseUtils::insertIntoTable
|
||||||
|
* @param table
|
||||||
|
* @param table_name
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
* @todo Loop VariantMap change to Loop over propertiess
|
||||||
|
*/
|
||||||
|
QSqlQuery DatabaseUtils::insertIntoTable(const DatabaseTable *table, const QString &table_name) const
|
||||||
|
{
|
||||||
|
QString columns;
|
||||||
|
QString values;
|
||||||
|
|
||||||
|
QVariantMap valueMap = table->toVariantMap();
|
||||||
|
|
||||||
|
// in: id / 23233
|
||||||
|
// out: columns: id
|
||||||
|
// out: values: :id
|
||||||
|
auto keys = valueMap.keys();
|
||||||
|
for (const auto& _key : keys) {
|
||||||
|
QString key = fromCamelCase(_key);
|
||||||
|
if (!columns.isEmpty()) {
|
||||||
|
columns += ", ";
|
||||||
|
values += ", ";
|
||||||
|
}
|
||||||
|
columns += key;
|
||||||
|
values += ":" + key;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(mDatabaseManager->database());
|
||||||
|
QString queryText = QString("INSERT INTO %1 ( %2 ) VALUES ( %3 );").arg(table_name).arg(columns).arg(values);
|
||||||
|
query.prepare(queryText);
|
||||||
|
|
||||||
|
for (const auto& _key : keys ) {
|
||||||
|
QString key = fromCamelCase(_key);
|
||||||
|
auto value = valueMap.value(_key);
|
||||||
|
query.bindValue(":"+key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery res = mDatabaseManager->executeQuery(query);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QSqlQuery DatabaseUtils::updateTable(const DatabaseTable *table, const QString &table_name, const QSet<QString> &filters) const
|
||||||
|
{
|
||||||
|
QString columnsAndValues;
|
||||||
|
QString primaryKeyCondition;
|
||||||
|
|
||||||
|
QVariantMap valueMap = table->toVariantMap(filters);
|
||||||
|
auto keys = valueMap.keys();
|
||||||
|
|
||||||
|
Q_ASSERT(!keys.isEmpty());
|
||||||
|
// Primary key is first user property ( first VariantMap item )
|
||||||
|
// in ( primary key ): id
|
||||||
|
// out: id = :id
|
||||||
|
const QString primaryKey = table->primaryKey();
|
||||||
|
primaryKeyCondition = primaryKey + " = :" + primaryKey;
|
||||||
|
|
||||||
|
for (const auto& _key : keys) {
|
||||||
|
QString key = fromCamelCase(_key);
|
||||||
|
if (!columnsAndValues.isEmpty()) {
|
||||||
|
columnsAndValues += ", ";
|
||||||
|
}
|
||||||
|
columnsAndValues += key + " = :" + key;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(mDatabaseManager->database());
|
||||||
|
QString queryText = QString("UPDATE %1 SET %2 WHERE %3;").arg(table_name).arg(columnsAndValues).arg(primaryKeyCondition);
|
||||||
|
query.prepare(queryText);
|
||||||
|
|
||||||
|
for (const auto& _key : keys ) {
|
||||||
|
QString key = fromCamelCase(_key);
|
||||||
|
auto value = valueMap.value(_key);
|
||||||
|
query.bindValue(":"+key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery res = mDatabaseManager->executeQuery(query);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QString DatabaseUtils::sqlType() const
|
||||||
|
{
|
||||||
|
return mSqlType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseUtils::setSqlType(const QString &sqlType)
|
||||||
|
{
|
||||||
|
mSqlType = sqlType;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseUtils::fromCamelCase(const QString &s)
|
||||||
|
{
|
||||||
|
static QRegularExpression regExp1 {"(.)([A-Z][a-z]+)"};
|
||||||
|
static QRegularExpression regExp2 {"([a-z0-9])([A-Z])"};
|
||||||
|
|
||||||
|
QString result = s;
|
||||||
|
result.replace(regExp1, "\\1_\\2");
|
||||||
|
result.replace(regExp2, "\\1_\\2");
|
||||||
|
|
||||||
|
return result.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString serversdk::DatabaseUtils::toCamelCase(const QString &s)
|
||||||
|
{
|
||||||
|
QStringList parts = s.split('_', QString::SkipEmptyParts);
|
||||||
|
for (int i=1; i<parts.size(); ++i)
|
||||||
|
parts[i].replace(0, 1, parts[i][0].toUpper());
|
||||||
|
|
||||||
|
return parts.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace serversdk
|
||||||
67
src/serversdk/databaseutils.h
Normal file
67
src/serversdk/databaseutils.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "databasetable.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlRecord>
|
||||||
|
|
||||||
|
class DatabaseUtilsTest;
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseManager;
|
||||||
|
|
||||||
|
class DatabaseUtils : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class ::DatabaseUtilsTest;
|
||||||
|
public:
|
||||||
|
explicit DatabaseUtils(DatabaseManager *manager = nullptr, QString type = "SQLITE");
|
||||||
|
|
||||||
|
QSqlQuery createTable(const DatabaseTable *table, const QString &table_name) const;
|
||||||
|
|
||||||
|
QSqlQuery insertIntoTable(const DatabaseTable *table, const QString &table_name) const;
|
||||||
|
|
||||||
|
QSqlQuery updateTable(const DatabaseTable *table, const QString &table_name, const QSet<QString> &filters = {}) const;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
QList<std::shared_ptr<T>> tableFromQuery(QSqlQuery query) const {
|
||||||
|
QList<std::shared_ptr<T>> list;
|
||||||
|
while (query.next()) {
|
||||||
|
auto rec = query.record();
|
||||||
|
int total = rec.count();
|
||||||
|
QMap<QString, QVariant> map;
|
||||||
|
for (int column = 0; column < total; ++column) {
|
||||||
|
QString columnName = rec.fieldName(column);
|
||||||
|
QString propertyName = toCamelCase(columnName);
|
||||||
|
map.insert(propertyName, query.value(column));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = std::make_shared<T>();
|
||||||
|
data->fromVariantMap(map);
|
||||||
|
list << data;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString sqlType() const;
|
||||||
|
void setSqlType(const QString &sqlType);
|
||||||
|
|
||||||
|
static QString fromCamelCase(const QString &s);
|
||||||
|
static QString toCamelCase(const QString& s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString createTableText(const DatabaseTable *table, const QString &table_name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DatabaseManager *mDatabaseManager = nullptr;
|
||||||
|
QString mSqlType;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace serversdk
|
||||||
|
|
||||||
@ -3,15 +3,22 @@
|
|||||||
|
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(sdkcore, "sdk.core");
|
Q_LOGGING_CATEGORY(sdkcore, "sdk.core");
|
||||||
Q_LOGGING_CATEGORY(sdknetwork, "sdk.network");
|
Q_LOGGING_CATEGORY(sdknetwork, "sdk.network");
|
||||||
Q_LOGGING_CATEGORY(sdkcompress, "sdk.compress");
|
Q_LOGGING_CATEGORY(sdkutils, "sdk.utils");
|
||||||
|
Q_LOGGING_CATEGORY(sdkutilsdb, "sdk.utils.db");
|
||||||
|
Q_LOGGING_CATEGORY(sdkdb, "sdk.db");
|
||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
Logger* Logger::sConsumer = nullptr;
|
Logger* Logger::sConsumer = nullptr;
|
||||||
//bool Logger::sShowFunction = false;
|
#ifdef Q_OS_UNIX
|
||||||
|
bool Logger::mColorOutput = true;
|
||||||
|
#else
|
||||||
|
bool Logger::mColorOutput = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void Logger::setConsumer(Logger *consumer)
|
void Logger::setConsumer(Logger *consumer)
|
||||||
@ -22,6 +29,7 @@ void Logger::setConsumer(Logger *consumer)
|
|||||||
Logger::Logger(QObject *parent) : QObject(parent)
|
Logger::Logger(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<LoggerMessage>();
|
qRegisterMetaType<LoggerMessage>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<LoggerMessage> Logger::getNewMessages(int currentCount)
|
QList<LoggerMessage> Logger::getNewMessages(int currentCount)
|
||||||
@ -42,6 +50,16 @@ QList<LoggerMessage> Logger::getNewMessages(int currentCount)
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Logger::toColor(const QString &color, const QString &text)
|
||||||
|
{
|
||||||
|
return color + text + "\033[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::setColorOutput(bool colorOutput)
|
||||||
|
{
|
||||||
|
mColorOutput = colorOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
{
|
{
|
||||||
@ -49,23 +67,29 @@ void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, co
|
|||||||
|
|
||||||
QString text;
|
QString text;
|
||||||
QString textType;
|
QString textType;
|
||||||
|
QString colorTextType;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QtDebugMsg:
|
case QtDebugMsg:
|
||||||
textType = QStringLiteral("debug");
|
textType = QStringLiteral("DEBUG");
|
||||||
|
colorTextType = Logger::toColor("\033[0;31m", textType);
|
||||||
break;
|
break;
|
||||||
case QtWarningMsg:
|
case QtWarningMsg:
|
||||||
textType = QStringLiteral("warning");
|
textType = QStringLiteral("WARN");
|
||||||
|
colorTextType = Logger::toColor("\033[0;33m", textType);
|
||||||
break;
|
break;
|
||||||
case QtCriticalMsg:
|
case QtCriticalMsg:
|
||||||
textType = QStringLiteral("critical");
|
textType = QStringLiteral("CRITICAL");
|
||||||
|
colorTextType = Logger::toColor("\033[0;31m", textType);
|
||||||
break;
|
break;
|
||||||
case QtFatalMsg:
|
case QtFatalMsg:
|
||||||
textType = QStringLiteral("fatal");
|
textType = QStringLiteral("FATAL");
|
||||||
|
colorTextType = Logger::toColor("\033[0;31m", textType);
|
||||||
break;
|
break;
|
||||||
#if QT_VERSION >= 0x050500
|
#if QT_VERSION >= 0x050500
|
||||||
case QtInfoMsg:
|
case QtInfoMsg:
|
||||||
textType = QStringLiteral("info");
|
textType = QStringLiteral("INFO");
|
||||||
|
colorTextType = Logger::toColor("\033[0;34m", textType);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -77,14 +101,19 @@ void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, co
|
|||||||
// Output message
|
// Output message
|
||||||
//text += QString("%1 %2:%3 %4").arg(context.category).arg(shortFile).arg(context.line).arg(msg);
|
//text += QString("%1 %2:%3 %4").arg(context.category).arg(shortFile).arg(context.line).arg(msg);
|
||||||
// New output syntax
|
// New output syntax
|
||||||
text = QStringLiteral("[%1] %2 [ %3:%4 | %5 | %6 ]")
|
|
||||||
.arg(textType, msg, shortFile)
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
.arg(context.line)
|
|
||||||
.arg(context.function, context.category);
|
const QString textTypeUni = mColorOutput ? colorTextType : textType;
|
||||||
|
|
||||||
|
text = QStringLiteral("[%1 %2 %3] %4") // [ %3:%4 | %5 | %6 ]")
|
||||||
|
.arg(now.toString("dd-MM-yyyy hh:mm:ss.z"))
|
||||||
|
.arg(textTypeUni)
|
||||||
|
.arg(context.category)
|
||||||
|
.arg(msg);
|
||||||
|
|
||||||
QTextStream out(stderr);
|
QTextStream out(stderr);
|
||||||
out << text << "\n"; //.toLocal8Bit() + "\n";
|
out << text << "\n"; //.toLocal8Bit() + "\n";
|
||||||
//out.flush(); // INVSDK-37 ORK-102 crash on windows when flush() -> possible threading problem
|
|
||||||
|
|
||||||
if (type == QtFatalMsg)
|
if (type == QtFatalMsg)
|
||||||
abort();
|
abort();
|
||||||
|
|||||||
@ -10,12 +10,13 @@
|
|||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(sdkcore)
|
Q_DECLARE_LOGGING_CATEGORY(sdkcore)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(sdknetwork)
|
Q_DECLARE_LOGGING_CATEGORY(sdknetwork)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(sdkcompress)
|
Q_DECLARE_LOGGING_CATEGORY(sdkutils)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(sdkutilsdb)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(sdkdb)
|
||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
/**
|
/** # Loggger
|
||||||
* # Loggger
|
|
||||||
*
|
*
|
||||||
* Class for logging qDebug qWarning qInfo and qCritical messages
|
* Class for logging qDebug qWarning qInfo and qCritical messages
|
||||||
*
|
*
|
||||||
@ -44,13 +45,27 @@ public:
|
|||||||
// User defined consumer ( messages will be sent there using insertMessage )
|
// User defined consumer ( messages will be sent there using insertMessage )
|
||||||
static Logger* sConsumer;
|
static Logger* sConsumer;
|
||||||
|
|
||||||
/// Default constructor
|
/**
|
||||||
|
* # Default values
|
||||||
|
*
|
||||||
|
* * bits - Expected 32bits
|
||||||
|
* * subjectId - must be valid ( not possible to add token withou valid subject )
|
||||||
|
*
|
||||||
|
* @m_class{m-block m-error}
|
||||||
|
|
||||||
|
@par Third-party license info
|
||||||
|
This utility depends on the [Magnum engine](https://magnum.graphics).
|
||||||
|
It's licensed under MIT, so all you need to do is mention it in the
|
||||||
|
credits of your commercial app.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
explicit Logger(QObject *parent = nullptr);
|
explicit Logger(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief User specific consumer of logger messages
|
* @brief User specific consumer of logger messages
|
||||||
* @param consumer - sublass of serversdk::Loger
|
* @param consumer - sublass of serversdk::Logger
|
||||||
*
|
*
|
||||||
* @code
|
* @code
|
||||||
* #include "core.h"
|
* #include "core.h"
|
||||||
@ -76,17 +91,22 @@ public:
|
|||||||
*/
|
*/
|
||||||
static void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
static void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||||
|
|
||||||
/** Pusing messges from installed message handler */
|
/** Pushing messges from installed message handler */
|
||||||
virtual void pushMessage(const LoggerMessage& logmsg);
|
virtual void pushMessage(const LoggerMessage& logmsg);
|
||||||
QList<serversdk::LoggerMessage> getNewMessages(int currentCount);
|
QList<serversdk::LoggerMessage> getNewMessages(int currentCount);
|
||||||
|
|
||||||
|
static QString toColor(const QString &color, const QString &text);
|
||||||
|
|
||||||
|
/** I will enable color output for terminal ( linux only ) */
|
||||||
|
void setColorOutput(bool colorOutput);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void messageReceived(const serversdk::LoggerMessage &message);
|
void messageReceived(const serversdk::LoggerMessage &message);
|
||||||
void messageListUpdated();
|
void messageListUpdated();
|
||||||
private:
|
private:
|
||||||
QList<LoggerMessage> mLogMessages;
|
QList<LoggerMessage> mLogMessages; //!< Test
|
||||||
QMutex mMutex;
|
QMutex mMutex;
|
||||||
|
static bool mColorOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -6,18 +6,25 @@
|
|||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
|
Request::Request(QString type, QString url, QByteArray requestData)
|
||||||
Request::~Request()
|
: mType(std::move(type)), mUrl(std::move(url)), mRequestData(std::move(requestData))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Request::~Request()
|
||||||
|
{
|
||||||
|
if (mNetworkReply)
|
||||||
|
mNetworkReply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkRequest Request::networkRequest() const
|
QNetworkRequest Request::networkRequest() const
|
||||||
{
|
{
|
||||||
QNetworkRequest request(QUrl(this->mUrl));
|
QNetworkRequest request(QUrl(this->mUrl));
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
|
||||||
|
|
||||||
if ( !userName().isEmpty() && !password().isEmpty() )
|
if ( !userName().isEmpty() ) {
|
||||||
request.setRawHeader("Authorization", this->basicAuthorization().toUtf8());
|
request.setRawHeader("Authorization", this->basicAuthorization().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
if ( !token().isEmpty() ) {
|
if ( !token().isEmpty() ) {
|
||||||
request.setRawHeader("Authorization", this->bearerAuthorization().toUtf8());
|
request.setRawHeader("Authorization", this->bearerAuthorization().toUtf8());
|
||||||
@ -28,17 +35,53 @@ QNetworkRequest Request::networkRequest() const
|
|||||||
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Enabling HTTP/2 ( not supported on Ubuntu 16.04 )
|
// Enabling HTTP/2 ( does not work on Qt 5.9.8 )
|
||||||
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Request::appendData(const QByteArray &array)
|
int Request::status() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(mNetworkReply);
|
||||||
|
|
||||||
|
if (!mNetworkReply->isFinished())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto variant = mNetworkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
if (!variant.isValid()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return variant.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Request::appendResponseData(const QByteArray &array)
|
||||||
{
|
{
|
||||||
mResponseData += array;
|
mResponseData += array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Request::setNetworkReply(QNetworkReply *networkReply)
|
||||||
|
{
|
||||||
|
Q_ASSERT(networkReply);
|
||||||
|
mNetworkReply = networkReply;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply *Request::networkReply() const
|
||||||
|
{
|
||||||
|
return mNetworkReply;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Request::type() const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setType(const QString &type)
|
||||||
|
{
|
||||||
|
mType = type;
|
||||||
|
}
|
||||||
|
|
||||||
QString Request::url() const
|
QString Request::url() const
|
||||||
{
|
{
|
||||||
return mUrl;
|
return mUrl;
|
||||||
@ -101,7 +144,6 @@ QString Request::basicAuthorization() const
|
|||||||
QByteArray data = concatenated.toLocal8Bit().toBase64();
|
QByteArray data = concatenated.toLocal8Bit().toBase64();
|
||||||
QString headerData = "Basic " + data;
|
QString headerData = "Basic " + data;
|
||||||
return headerData;
|
return headerData;
|
||||||
//request.setRawHeader("Authorization", headerData.toLocal8Bit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Request::bearerAuthorization() const
|
QString Request::bearerAuthorization() const
|
||||||
@ -110,4 +152,19 @@ QString Request::bearerAuthorization() const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qint64 Request::getProcessTime()
|
||||||
|
{
|
||||||
|
if (!mStartTime.isNull() && !mFinishTime.isNull()) {
|
||||||
|
return mStartTime.msecsTo(mFinishTime);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
} // core
|
} // core
|
||||||
|
|
||||||
|
|
||||||
|
QDebug operator <<(QDebug dbg, serversdk::Request *request)
|
||||||
|
{
|
||||||
|
dbg.noquote() << request->type() << request->url() << "data:" << request->requestData() << "header:" << request->networkReply()->rawHeaderList();
|
||||||
|
return dbg.maybeSpace();
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
@ -12,10 +15,13 @@ class Request : public QObject {
|
|||||||
public:
|
public:
|
||||||
Request() = default;
|
Request() = default;
|
||||||
~Request() override;
|
~Request() override;
|
||||||
Request(QString type, QString url, QByteArray responseData = QByteArray());
|
Request(QString type, QString url, QByteArray requestData = QByteArray());
|
||||||
virtual QNetworkRequest networkRequest() const;
|
virtual QNetworkRequest networkRequest() const;
|
||||||
|
|
||||||
// Data handling data
|
// Data handling data
|
||||||
|
int status() const;
|
||||||
|
QNetworkReply *networkReply() const;
|
||||||
|
void setNetworkReply(QNetworkReply *networkReply);
|
||||||
QByteArray requestData() const;
|
QByteArray requestData() const;
|
||||||
void setRequestData(const QByteArray &requestData);
|
void setRequestData(const QByteArray &requestData);
|
||||||
QByteArray responseData() const;
|
QByteArray responseData() const;
|
||||||
@ -23,8 +29,8 @@ public:
|
|||||||
// Attributes
|
// Attributes
|
||||||
QString url() const;
|
QString url() const;
|
||||||
void setUrl(const QString &url);
|
void setUrl(const QString &url);
|
||||||
|
QString type() const;
|
||||||
|
void setType(const QString &type);
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
QString userName() const;
|
QString userName() const;
|
||||||
@ -38,17 +44,22 @@ public:
|
|||||||
QString basicAuthorization() const;
|
QString basicAuthorization() const;
|
||||||
QString bearerAuthorization() const;
|
QString bearerAuthorization() const;
|
||||||
|
|
||||||
|
qint64 getProcessTime();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Additional attributes
|
||||||
|
void logStartTime() { mStartTime = QDateTime::currentDateTime(); }
|
||||||
|
void logFinishTime() { mFinishTime = QDateTime::currentDateTime(); }
|
||||||
// Data handling
|
// Data handling
|
||||||
void appendData(const QByteArray &array);
|
void appendResponseData(const QByteArray &array);
|
||||||
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished();
|
void finished(std::shared_ptr<Request> request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString mType; // POST, GET
|
QString mType = "GET"; // POST, GET
|
||||||
QString mUrl;
|
QString mUrl;
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
@ -57,8 +68,18 @@ private:
|
|||||||
QString mToken;
|
QString mToken;
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
|
QNetworkReply *mNetworkReply = nullptr;
|
||||||
QByteArray mRequestData;
|
QByteArray mRequestData;
|
||||||
QByteArray mResponseData;
|
QByteArray mResponseData;
|
||||||
|
|
||||||
|
// Additional attributes
|
||||||
|
QDateTime mStartTime;
|
||||||
|
QDateTime mFinishTime;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // core
|
} // core
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, serversdk::Request *request);
|
||||||
|
|||||||
@ -19,39 +19,73 @@ RequestManager::RequestManager(QObject *parent)
|
|||||||
connect(&mNetworkAccessManager, &QNetworkAccessManager::authenticationRequired, [](){
|
connect(&mNetworkAccessManager, &QNetworkAccessManager::authenticationRequired, [](){
|
||||||
qCWarning(sdknetwork) << "server need authentication";
|
qCWarning(sdknetwork) << "server need authentication";
|
||||||
});
|
});
|
||||||
|
connect(&mNetworkAccessManager, &QNetworkAccessManager::sslErrors, this, &RequestManager::sslErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process request using QNetworkAccessManger
|
||||||
|
*
|
||||||
|
* @param Request
|
||||||
|
*/
|
||||||
|
bool RequestManager::processRequest(const std::shared_ptr<Request>& request)
|
||||||
|
{
|
||||||
|
QNetworkReply *reply = nullptr;
|
||||||
|
request->logStartTime();
|
||||||
|
|
||||||
|
if (request->type() == "POST") {
|
||||||
|
if ( request->requestData().isEmpty()) {
|
||||||
|
qCWarning(sdknetwork) << "Request data is empty -> nothing to send";
|
||||||
|
}
|
||||||
|
reply = mNetworkAccessManager.post(request->networkRequest(), request->requestData());
|
||||||
|
} else if (request->type() == "GET") {
|
||||||
|
if ( ! request->requestData().isEmpty()) {
|
||||||
|
qCWarning(sdknetwork) << "Request data is not empty, and you are trying to use get ( not sending any data)";
|
||||||
|
}
|
||||||
|
reply = mNetworkAccessManager.get(request->networkRequest());
|
||||||
|
} else if (request->type() == "PUT") {
|
||||||
|
reply = mNetworkAccessManager.put(request->networkRequest(), request->requestData());
|
||||||
|
} else {
|
||||||
|
qCritical(sdknetwork) << "Using request type:" << request->type() << "is not implemented";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
Q_ASSERT(reply);
|
||||||
* @brief Soap::downloadRequest
|
// Adding reply to final request
|
||||||
* @param soapRequest
|
reply->setParent(nullptr); // ensure QNetworkreply wont be deleted by QNetworkAccessManager ( it is parent of reply )
|
||||||
*/
|
request->setNetworkReply(reply);
|
||||||
void RequestManager::processRequest(const std::shared_ptr<Request>& request)
|
|
||||||
{
|
// Add to map to get correct request when reply is used in signal
|
||||||
QNetworkReply *reply = mNetworkAccessManager.post(request->networkRequest(), request->requestData());
|
|
||||||
mRequestMap.insert(reply, request);
|
mRequestMap.insert(reply, request);
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, this, &RequestManager::sslErrors);
|
connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
|
||||||
|
this, &RequestManager::replyError);
|
||||||
connect(reply, &QNetworkReply::downloadProgress, this, &RequestManager::downloadProgress);
|
connect(reply, &QNetworkReply::downloadProgress, this, &RequestManager::downloadProgress);
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [=]() {
|
connect(reply, &QNetworkReply::finished, this, [=]() {
|
||||||
std::shared_ptr<Request> request = mRequestMap.value(reply);
|
std::shared_ptr<Request> request = mRequestMap.value(reply);
|
||||||
|
request->logFinishTime();
|
||||||
mRequestMap.remove(reply);
|
mRequestMap.remove(reply);
|
||||||
reply->deleteLater();
|
emit request->finished(request);
|
||||||
emit request->finished();
|
|
||||||
emit downloadFinished(request);
|
emit downloadFinished(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle sslErrors -> only debug output
|
* @brief Handle sslErrors -> only debug output
|
||||||
* @param sslErrors ( from QNetworkReply::sslErrors )
|
* @param sslErrors ( from QNetworkReply::sslErrors )
|
||||||
*/
|
*/
|
||||||
void RequestManager::sslErrors(const QList<QSslError> &sslErrors)
|
void RequestManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &sslErrors)
|
||||||
{
|
{
|
||||||
for (const QSslError &error : sslErrors) {
|
for (const QSslError &error : sslErrors) {
|
||||||
qCDebug(sdknetwork) << QStringLiteral("SSL error:") << error.errorString();
|
qCDebug(sdknetwork) << QStringLiteral("SSL error:") << error.errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ignoreSsl()) {
|
||||||
|
reply->ignoreSslErrors(sslErrors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +110,6 @@ void RequestManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|||||||
encoding = pair.second;
|
encoding = pair.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray data = reply->readAll();
|
QByteArray data = reply->readAll();
|
||||||
|
|
||||||
// Decompress gzip data
|
// Decompress gzip data
|
||||||
@ -87,9 +120,29 @@ void RequestManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Append data to request
|
// Append data to request
|
||||||
request->appendData(data);
|
request->appendResponseData(data);
|
||||||
|
|
||||||
emit progresss(bytesReceived, bytesTotal);
|
emit progresss(bytesReceived, bytesTotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestManager::replyError(QNetworkReply::NetworkError code)
|
||||||
|
{
|
||||||
|
auto *reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
Q_ASSERT(mRequestMap.contains(reply));
|
||||||
|
std::shared_ptr<Request> request = mRequestMap.value(reply);
|
||||||
|
|
||||||
|
|
||||||
|
qCWarning(sdknetwork) << request.get() << "reply error: " << code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestManager::ignoreSsl() const
|
||||||
|
{
|
||||||
|
return mIgnoreSsl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestManager::setIgnoreSsl(bool ignoreSsl)
|
||||||
|
{
|
||||||
|
mIgnoreSsl = ignoreSsl;
|
||||||
|
}
|
||||||
|
|
||||||
} // core
|
} // core
|
||||||
|
|||||||
@ -19,13 +19,16 @@ class RequestManager : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit RequestManager(QObject *parent = nullptr);
|
explicit RequestManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
void processRequest(const std::shared_ptr<Request>& request);
|
bool processRequest(const std::shared_ptr<Request>& request);
|
||||||
|
|
||||||
|
|
||||||
|
bool ignoreSsl() const;
|
||||||
|
void setIgnoreSsl(bool ignoreSsl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_SLOT void sslErrors(const QList<QSslError> &sslErrors);
|
Q_SLOT void sslErrors(QNetworkReply *reply, const QList<QSslError> &sslErrors);
|
||||||
Q_SLOT void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
Q_SLOT void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
Q_SLOT void replyError(QNetworkReply::NetworkError code);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void downloadFinished(std::shared_ptr<Request> request);
|
void downloadFinished(std::shared_ptr<Request> request);
|
||||||
@ -34,6 +37,7 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QNetworkAccessManager mNetworkAccessManager;
|
QNetworkAccessManager mNetworkAccessManager;
|
||||||
QMap<QNetworkReply*, std::shared_ptr<Request>> mRequestMap;
|
QMap<QNetworkReply*, std::shared_ptr<Request>> mRequestMap;
|
||||||
|
bool mIgnoreSsl = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // core
|
} // core
|
||||||
|
|||||||
@ -7,21 +7,29 @@ CONFIG += c++14
|
|||||||
|
|
||||||
INCLUDEPATH += $$PWD
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
#unix: DEFINES += USE_ZLIB
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/core.cpp \
|
core.cpp \
|
||||||
$$PWD/databasemanager.cpp \
|
databasemanager.cpp \
|
||||||
$$PWD/logger.cpp \
|
databaseutils.cpp \
|
||||||
$$PWD/loggermessage.cpp \
|
logger.cpp \
|
||||||
$$PWD/requestmanager.cpp \
|
loggermessage.cpp \
|
||||||
$$PWD/request.cpp \
|
requestmanager.cpp \
|
||||||
$$PWD/soaprequest.cpp
|
request.cpp \
|
||||||
|
utils.cpp \
|
||||||
|
soaprequest.cpp \
|
||||||
|
databasetable.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/core.h \
|
core.h \
|
||||||
$$PWD/databasemanager.h \
|
databasemanager.h \
|
||||||
$$PWD/logger.h \
|
databaseutils.h \
|
||||||
$$PWD/loggermessage.h \
|
logger.h \
|
||||||
$$PWD/requestmanager.h \
|
loggermessage.h \
|
||||||
$$PWD/request.h \
|
requestmanager.h \
|
||||||
$$PWD/soaprequest.h
|
request.h \
|
||||||
|
utils.h \
|
||||||
|
soaprequest.h \
|
||||||
|
databasetable.h
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
#include "soaprequest.h"
|
#include "soaprequest.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
SoapRequest::SoapRequest(QString action, QString endpoint, QByteArray data)
|
SoapRequest::SoapRequest(QString action, const QString& endpoint, const QByteArray& data)
|
||||||
: mSoapAction(action)
|
: mSoapAction(std::move(action))
|
||||||
{
|
{
|
||||||
|
setType("POST");
|
||||||
setUrl(endpoint);
|
setUrl(endpoint);
|
||||||
setRequestData(data);
|
setRequestData(data);
|
||||||
}
|
}
|
||||||
@ -12,7 +15,7 @@ SoapRequest::SoapRequest(QString action, QString endpoint, QByteArray data)
|
|||||||
QNetworkRequest SoapRequest::networkRequest() const
|
QNetworkRequest SoapRequest::networkRequest() const
|
||||||
{
|
{
|
||||||
Q_ASSERT(!this->url().isEmpty());
|
Q_ASSERT(!this->url().isEmpty());
|
||||||
QNetworkRequest request(QUrl(this->url()));
|
QNetworkRequest request = Request::networkRequest();
|
||||||
|
|
||||||
// Have to set header
|
// Have to set header
|
||||||
QString header;
|
QString header;
|
||||||
@ -32,9 +35,6 @@ QNetworkRequest SoapRequest::networkRequest() const
|
|||||||
// Enabled compression
|
// Enabled compression
|
||||||
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
||||||
|
|
||||||
// Enabling HTTP/2 ( not supported on Ubuntu 16.04 )
|
|
||||||
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,9 @@ namespace serversdk {
|
|||||||
|
|
||||||
class SoapRequest : public Request
|
class SoapRequest : public Request
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SoapRequest(QString action, QString endpoint, QByteArray data);
|
SoapRequest(QString action, const QString& endpoint, const QByteArray& data);
|
||||||
QNetworkRequest networkRequest() const override;
|
QNetworkRequest networkRequest() const override;
|
||||||
|
|
||||||
QString soapAction() const;
|
QString soapAction() const;
|
||||||
|
|||||||
@ -1,22 +1,25 @@
|
|||||||
#include "compression.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
#ifdef USE_ZLIB
|
||||||
#include <zlib.h> // gzip decompress
|
#include <zlib.h> // gzip decompress
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
|
||||||
namespace core {
|
#ifdef USE_ZLIB
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Decompress gzip data
|
* @brief Decompress gzip data
|
||||||
* @param compressed gzip data
|
* @param compressed gzip data
|
||||||
* @return decompressed data
|
* @return decompressed data
|
||||||
*/
|
*/
|
||||||
QByteArray Compression::decompress(const QByteArray &compressed)
|
QByteArray Utils::decompress(const QByteArray &compressed)
|
||||||
{
|
{
|
||||||
if (compressed.size() <= 4) {
|
if (compressed.size() <= 4) {
|
||||||
qWarning("gUncompress: Input data is truncated");
|
qCCritical(sdkutils) << "gUncompress: Input data is truncated";
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,46 +71,13 @@ QByteArray Compression::decompress(const QByteArray &compressed)
|
|||||||
float ratio = (float)compressed.count() / (float)result.count();
|
float ratio = (float)compressed.count() / (float)result.count();
|
||||||
float compression = ratio * 100.0;
|
float compression = ratio * 100.0;
|
||||||
|
|
||||||
qCInfo(sdkcompress) << "compress data:" << compressed.count()
|
qCInfo(sdkutils) << "compress data:" << compressed.count()
|
||||||
<< "uncompressed data:" << result.count()
|
<< "uncompressed data:" << result.count()
|
||||||
<< "compression:" << compression;
|
<< "compression:" << compression;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/**
|
|
||||||
* @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
|
} // namespace core
|
||||||
18
src/serversdk/utils.h
Normal file
18
src/serversdk/utils.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
static QByteArray decompress(const QByteArray &compressed);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace serversdk
|
||||||
@ -3,22 +3,13 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
serversdk::initAppLogger();
|
serversdk::initAppLogger();
|
||||||
|
|
||||||
qCDebug(sdkcore) << "test message";
|
qCDebug(sdkcore) << "Application starting";
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
QCoreApplication::setApplicationName("colnod-connector");
|
QCoreApplication::setApplicationName("example-app");
|
||||||
QCoreApplication::setApplicationVersion("1.0");
|
QCoreApplication::setApplicationVersion("0.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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
13
test/auto/auto.pro
Normal file
13
test/auto/auto.pro
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS += \
|
||||||
|
request/tst_soap.pro \
|
||||||
|
request/tst_request.pro \
|
||||||
|
request/tst_requestmanager.pro \
|
||||||
|
database/tst_databasemanager.pro \
|
||||||
|
database/tst_sqlite.pro \
|
||||||
|
database/tst_postgresql.pro \
|
||||||
|
database/tst_databasetable.pro \
|
||||||
|
database/tst_databaseutils.pro \
|
||||||
|
|
||||||
|
unix: SUBDIRS += tst_utils
|
||||||
36
test/auto/database/tst_databasemanager.cpp
Normal file
36
test/auto/database/tst_databasemanager.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "serversdk/databasemanager.h"
|
||||||
|
// add necessary includes here
|
||||||
|
|
||||||
|
class DatabaseTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DatabaseTest();
|
||||||
|
~DatabaseTest();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void createDb_test();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DatabaseTest::DatabaseTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseTest::~DatabaseTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::createDb_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(DatabaseTest)
|
||||||
|
|
||||||
|
#include "tst_databasemanager.moc"
|
||||||
11
test/auto/database/tst_databasemanager.pro
Normal file
11
test/auto/database/tst_databasemanager.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_databasemanager.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
169
test/auto/database/tst_databasetable.cpp
Normal file
169
test/auto/database/tst_databasetable.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class BookTable : public serversdk::DatabaseTable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, uuid, setUuid, QString());
|
||||||
|
TABLE_PROPERTY(int, id, setId, 0);
|
||||||
|
TABLE_PROPERTY(QString, name, setName, "Test");
|
||||||
|
TABLE_PROPERTY(QString, author, setAuthor, "Me");
|
||||||
|
public:
|
||||||
|
QByteArray filteredJson() {
|
||||||
|
auto variant = this->toVariantMap();
|
||||||
|
variant.remove("author");
|
||||||
|
QJsonDocument doc = QJsonDocument::fromVariant(variant);
|
||||||
|
return doc.toJson(QJsonDocument::Compact);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QString enumjson = R"({"authErrors":255,"flags":["Enabled","NoValidity"],"subjectId":"1438aade-3aa6-4885-8e0f-138dedae727a"})";
|
||||||
|
|
||||||
|
|
||||||
|
class EnumTest : public serversdk::DatabaseTable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(int, authErrors, setAuthErrors, 0);
|
||||||
|
TABLE_PROPERTY(bool, enabled, setEnabled, false);
|
||||||
|
TABLE_PROPERTY(bool, noValidity, setNoValidity, false);
|
||||||
|
TABLE_PROPERTY(QString, subjectId, setSubjectId, QString());
|
||||||
|
public:
|
||||||
|
QByteArray jsonData(const QSet<QString> &filters = QSet<QString>()) const override
|
||||||
|
{
|
||||||
|
auto variantMap = this->toVariantMap(filters);
|
||||||
|
auto enabled = variantMap["enabled"].toBool();
|
||||||
|
auto noValidity = variantMap["noValidity"].toBool();
|
||||||
|
variantMap.remove("enabled");
|
||||||
|
variantMap.remove("noValidity");
|
||||||
|
QVariantList flags;
|
||||||
|
if ( enabled ) {
|
||||||
|
flags << "Enabled";
|
||||||
|
}
|
||||||
|
if ( noValidity ) {
|
||||||
|
flags << "NoValidity";
|
||||||
|
}
|
||||||
|
variantMap.insert("flags", flags);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromVariant(variantMap);
|
||||||
|
return doc.toJson(QJsonDocument::Compact);
|
||||||
|
}
|
||||||
|
bool fromJson(const QByteArray &data) override
|
||||||
|
{
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
QVariantMap variant = qvariant_cast<QVariantMap>(doc.toVariant());
|
||||||
|
|
||||||
|
QVariantList flags = variant["flags"].toList();
|
||||||
|
for (auto value : flags) {
|
||||||
|
if ( value.toString() == "Enabled") {
|
||||||
|
variant.insert("enabled", true);
|
||||||
|
}
|
||||||
|
if ( value.toString() == "NoValidity") {
|
||||||
|
variant.insert("noValidity", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variant.remove("flags");
|
||||||
|
|
||||||
|
// Populate this object data
|
||||||
|
bool res = fromVariantMap(variant);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseTableTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DatabaseTableTest();
|
||||||
|
~DatabaseTableTest();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void constructor_test();
|
||||||
|
void qvariant_test();
|
||||||
|
void json_test();
|
||||||
|
void enum_test();
|
||||||
|
|
||||||
|
void filter_json_test();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DatabaseTableTest::DatabaseTableTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseTableTest::~DatabaseTableTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTableTest::constructor_test()
|
||||||
|
{
|
||||||
|
auto table = std::make_unique<BookTable>();
|
||||||
|
QCOMPARE(table->id(), 0);
|
||||||
|
QCOMPARE(table->name(), QStringLiteral("Test"));
|
||||||
|
|
||||||
|
QCOMPARE(table->primaryKey(), QStringLiteral("uuid"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DatabaseTableTest::qvariant_test()
|
||||||
|
{
|
||||||
|
auto table = std::make_unique<BookTable>();
|
||||||
|
QVariantMap map = table->toVariantMap();
|
||||||
|
QCOMPARE(map.count(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTableTest::json_test()
|
||||||
|
{
|
||||||
|
auto table = std::make_unique<BookTable>();
|
||||||
|
QCOMPARE(table->id(), 0);
|
||||||
|
QCOMPARE(table->name(), QStringLiteral("Test"));
|
||||||
|
|
||||||
|
QByteArray expected(R"({"author":"Me","id":0,"name":"Test","uuid":""})");
|
||||||
|
QCOMPARE(table->jsonData(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTableTest::enum_test()
|
||||||
|
{
|
||||||
|
auto table = std::make_unique<EnumTest>();
|
||||||
|
table->setAuthErrors(255);
|
||||||
|
table->setNoValidity(true);
|
||||||
|
table->setEnabled(true);
|
||||||
|
table->setSubjectId("1438aade-3aa6-4885-8e0f-138dedae727a");
|
||||||
|
|
||||||
|
QCOMPARE(table->jsonData(), enumjson.toUtf8());
|
||||||
|
|
||||||
|
EnumTest jsonTest;
|
||||||
|
|
||||||
|
jsonTest.fromJson(enumjson.toUtf8());
|
||||||
|
|
||||||
|
QCOMPARE(jsonTest.authErrors(), 255);
|
||||||
|
QCOMPARE(jsonTest.noValidity(), true);
|
||||||
|
QCOMPARE(jsonTest.enabled(), true);
|
||||||
|
QCOMPARE(jsonTest.subjectId(), QStringLiteral("1438aade-3aa6-4885-8e0f-138dedae727a"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Baseclass must not contain property of derived class ( extended class ) */
|
||||||
|
void DatabaseTableTest::filter_json_test()
|
||||||
|
{
|
||||||
|
auto book = std::make_shared<BookTable>();
|
||||||
|
QCOMPARE(book->jsonData(), QByteArray(R"({"author":"Me","id":0,"name":"Test","uuid":""})"));
|
||||||
|
|
||||||
|
|
||||||
|
const QByteArray filtered(R"({"id":0,"name":"Test","uuid":""})");
|
||||||
|
QCOMPARE(book->filteredJson(), filtered);
|
||||||
|
QCOMPARE(book->jsonData({"author"}), filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(DatabaseTableTest)
|
||||||
|
|
||||||
|
#include "tst_databasetable.moc"
|
||||||
11
test/auto/database/tst_databasetable.pro
Normal file
11
test/auto/database/tst_databasetable.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_databasetable.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
85
test/auto/database/tst_databaseutils.cpp
Normal file
85
test/auto/database/tst_databaseutils.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "serversdk/databasemanager.h"
|
||||||
|
#include "serversdk/databaseutils.h"
|
||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
class BookTable : public serversdk::DatabaseTable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QUuid, id, set_id, QUuid());
|
||||||
|
TABLE_PROPERTY(QString, description, set_description, "");
|
||||||
|
TABLE_PROPERTY(QDateTime, last_update, set_last_update, QDateTime::fromSecsSinceEpoch(0));
|
||||||
|
TABLE_PROPERTY(QString, name, set_name, QString());
|
||||||
|
TABLE_PROPERTY(QString, pin, set_pin, QString());
|
||||||
|
TABLE_PROPERTY(QDateTime, last_modified, set_last_modified, QDateTime::fromSecsSinceEpoch(0));
|
||||||
|
TABLE_PROPERTY(bool, deleted, set_deleted, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseUtilsTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DatabaseUtilsTest();
|
||||||
|
~DatabaseUtilsTest();
|
||||||
|
QString uuid() const;
|
||||||
|
void setUuid(QString value);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void createTable_test();
|
||||||
|
void toCamelCase_test();
|
||||||
|
void fromCamelCase_test();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DatabaseUtilsTest::DatabaseUtilsTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseUtilsTest::~DatabaseUtilsTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseUtilsTest::createTable_test()
|
||||||
|
{
|
||||||
|
auto table = std::make_unique<BookTable>();
|
||||||
|
|
||||||
|
auto db = std::make_unique<serversdk::DatabaseManager>();
|
||||||
|
serversdk::DatabaseUtils utils(db.get());
|
||||||
|
QString sql = utils.createTableText(table.get(), "subjects");
|
||||||
|
|
||||||
|
QString expected = "CREATE TABLE subjects ( "
|
||||||
|
"id TEXT PRIMARY KEY NOT NULL, "
|
||||||
|
"description TEXT, "
|
||||||
|
"last_update INTEGER, "
|
||||||
|
"name TEXT, "
|
||||||
|
"pin TEXT, "
|
||||||
|
"last_modified INTEGER, "
|
||||||
|
"deleted INTEGER )";
|
||||||
|
|
||||||
|
QCOMPARE(sql, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseUtilsTest::toCamelCase_test()
|
||||||
|
{
|
||||||
|
QString test1 = "id_name";
|
||||||
|
QCOMPARE(serversdk::DatabaseUtils::toCamelCase(test1), QStringLiteral("idName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseUtilsTest::fromCamelCase_test()
|
||||||
|
{
|
||||||
|
QString test1 = "idName";
|
||||||
|
QCOMPARE(serversdk::DatabaseUtils::fromCamelCase(test1), QStringLiteral("id_name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(DatabaseUtilsTest)
|
||||||
|
|
||||||
|
#include "tst_databaseutils.moc"
|
||||||
11
test/auto/database/tst_databaseutils.pro
Normal file
11
test/auto/database/tst_databaseutils.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_databaseutils.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
105
test/auto/database/tst_postgresql.cpp
Normal file
105
test/auto/database/tst_postgresql.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "serversdk/databasemanager.h"
|
||||||
|
#include "serversdk/databaseutils.h"
|
||||||
|
#include "serversdk/core.h"
|
||||||
|
// add necessary includes here
|
||||||
|
|
||||||
|
|
||||||
|
class Name : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(int, id, set_id, 0);
|
||||||
|
TABLE_PROPERTY(QString, name, set_name, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PostgresqlTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PostgresqlTest();
|
||||||
|
~PostgresqlTest();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void opendb_test();
|
||||||
|
void createTable_test();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
PostgresqlTest::PostgresqlTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PostgresqlTest::~PostgresqlTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostgresqlTest::opendb_test()
|
||||||
|
{
|
||||||
|
// Prepare temporary path for database
|
||||||
|
const QString host = "192.168.1.94";
|
||||||
|
const QString databaseName = "serversdk";
|
||||||
|
const QString user = "postgres";
|
||||||
|
const QString password = "34rjkciea12";
|
||||||
|
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openPostgres(host, databaseName, user, password);
|
||||||
|
QVERIFY(db.isValid());
|
||||||
|
QVERIFY(db.isOpen());
|
||||||
|
|
||||||
|
// Closing database
|
||||||
|
db.close();
|
||||||
|
QVERIFY(!db.isOpen());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostgresqlTest::createTable_test()
|
||||||
|
{
|
||||||
|
// Prepare temporary path for database
|
||||||
|
const QString host = "192.168.1.94";
|
||||||
|
const QString databaseName = "serversdk";
|
||||||
|
const QString user = "postgres";
|
||||||
|
const QString password = "34rjkciea12";
|
||||||
|
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openPostgres(host, databaseName, user, password);
|
||||||
|
QVERIFY(db.isValid());
|
||||||
|
QVERIFY(db.isOpen());
|
||||||
|
|
||||||
|
Name names;
|
||||||
|
|
||||||
|
//Have to drop tabel
|
||||||
|
QSqlQuery dropQuery = db.execPlainSql("DROP TABLE names");
|
||||||
|
//QVERIFY(dropQuery.isValid());
|
||||||
|
|
||||||
|
// CREATE TABLE
|
||||||
|
serversdk::DatabaseUtils utils(&db, "PSQL");
|
||||||
|
QSqlQuery query = utils.createTable(&names, "names");
|
||||||
|
//QVERIFY(query.isValid());
|
||||||
|
|
||||||
|
// INSERT INTO TABLE
|
||||||
|
names.set_id(2);
|
||||||
|
names.set_name("Testname");
|
||||||
|
|
||||||
|
utils.insertIntoTable(&names, "names");
|
||||||
|
|
||||||
|
// SELECT FROM TABLE
|
||||||
|
QSqlQuery selectSQL = db.execPlainSql("SELECT * FROM names");
|
||||||
|
auto list = utils.tableFromQuery<Name>(selectSQL);
|
||||||
|
|
||||||
|
// ENSURE SAME
|
||||||
|
auto first = list.first();
|
||||||
|
// Problem is that fromListvariantMap returns shared_ptr of class DataTable
|
||||||
|
// Must subclass.
|
||||||
|
auto names_tab = std::dynamic_pointer_cast<Name>(first);
|
||||||
|
QCOMPARE(list.count(), 1);
|
||||||
|
QCOMPARE(names_tab->name(), QStringLiteral("Testname"));
|
||||||
|
|
||||||
|
QCOMPARE(list.first()->jsonData(), names.jsonData());
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(PostgresqlTest)
|
||||||
|
|
||||||
|
#include "tst_postgresql.moc"
|
||||||
11
test/auto/database/tst_postgresql.pro
Normal file
11
test/auto/database/tst_postgresql.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_postgresql.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
182
test/auto/database/tst_sqlite.cpp
Normal file
182
test/auto/database/tst_sqlite.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "serversdk/databasemanager.h"
|
||||||
|
#include "serversdk/databaseutils.h"
|
||||||
|
#include "serversdk/core.h"
|
||||||
|
// add necessary includes here
|
||||||
|
|
||||||
|
class Name : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, uuid, set_uuid, QString());
|
||||||
|
TABLE_PROPERTY(int, id, set_id, 0);
|
||||||
|
TABLE_PROPERTY(QString, name, set_name, "");
|
||||||
|
TABLE_PROPERTY(QString, vehicleName, set_vehicle_name, "Škodouliňkaťoučký");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SqliteTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SqliteTest() = default;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void createDb_test();
|
||||||
|
void createTable_test();
|
||||||
|
void updateTable_test();
|
||||||
|
void fail_updateTable_test();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void SqliteTest::createDb_test()
|
||||||
|
{
|
||||||
|
// Prepare temporary path for database
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
QString dbpath = dir.path() + "/testdb.sqlite";
|
||||||
|
QVERIFY(!dbpath.isEmpty());
|
||||||
|
QVERIFY(!QFileInfo::exists(dbpath));
|
||||||
|
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openSQLite(dbpath);
|
||||||
|
QVERIFY(db.isValid());
|
||||||
|
QVERIFY(db.isOpen());
|
||||||
|
QVERIFY(QFileInfo::exists(dbpath));
|
||||||
|
|
||||||
|
// Closing database
|
||||||
|
db.close();
|
||||||
|
QVERIFY(!db.isOpen());
|
||||||
|
|
||||||
|
|
||||||
|
// Temporary dir cleanup
|
||||||
|
QVERIFY(dir.remove());
|
||||||
|
|
||||||
|
QVERIFY(!QFileInfo::exists(dbpath));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqliteTest::createTable_test()
|
||||||
|
{
|
||||||
|
// Prepare temporary path for database
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QString dbpath = dir.path() + "/testdb.sqlite";
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openSQLite(dbpath);
|
||||||
|
QVERIFY(db.isOpen());
|
||||||
|
|
||||||
|
Name name_A, name_B;
|
||||||
|
|
||||||
|
// CREATE TABLE
|
||||||
|
serversdk::DatabaseUtils utils(&db);
|
||||||
|
QSqlQuery query = utils.createTable(&name_A, "names");
|
||||||
|
|
||||||
|
// INSERT INTO TABLE
|
||||||
|
const QString uuidA = "48f64fab-ba2a-40b5-8fbb-75361099dfb6";
|
||||||
|
const QString uuidB = "d70143ab-8c14-4ee0-85cb-f0d1b72df104";
|
||||||
|
name_A.set_uuid(uuidA);
|
||||||
|
name_A.set_id(2);
|
||||||
|
name_A.set_name("TestnameA");
|
||||||
|
name_B.set_uuid(uuidB);
|
||||||
|
name_B.set_id(2);
|
||||||
|
name_B.set_name("TestnameB");
|
||||||
|
|
||||||
|
utils.insertIntoTable(&name_A, "names");
|
||||||
|
utils.insertIntoTable(&name_B, "names");
|
||||||
|
|
||||||
|
// SELECT FROM TABLE
|
||||||
|
QSqlQuery selectSQL = db.execPlainSql("SELECT * FROM names");
|
||||||
|
auto list = utils.tableFromQuery<Name>(selectSQL);
|
||||||
|
|
||||||
|
|
||||||
|
// ENSURE SAME
|
||||||
|
QCOMPARE(list.count(), 2);
|
||||||
|
auto names_tab = list.first();
|
||||||
|
qDebug() << "Uuid" << names_tab->uuid();
|
||||||
|
QVERIFY(names_tab);
|
||||||
|
QCOMPARE(names_tab->name(), QStringLiteral("TestnameA"));
|
||||||
|
|
||||||
|
QCOMPARE(list.first()->jsonData(), name_A.jsonData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqliteTest::updateTable_test()
|
||||||
|
{
|
||||||
|
// Prepare temporary path for database
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QString dbpath = dir.path() + "/testdb.sqlite";
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openSQLite(dbpath);
|
||||||
|
QVERIFY(db.isOpen());
|
||||||
|
|
||||||
|
Name name_A, name_B;
|
||||||
|
|
||||||
|
// CREATE TABLE
|
||||||
|
serversdk::DatabaseUtils utils(&db);
|
||||||
|
QSqlQuery query = utils.createTable(&name_A, "names");
|
||||||
|
|
||||||
|
// INSERT INTO TABLE
|
||||||
|
const QString uuidA = "48f64fab-ba2a-40b5-8fbb-75361099dfb6";
|
||||||
|
const QString uuidB = "d70143ab-8c14-4ee0-85cb-f0d1b72df104";
|
||||||
|
name_A.set_uuid(uuidA);
|
||||||
|
name_A.set_id(2);
|
||||||
|
name_A.set_name("TestnameA");
|
||||||
|
name_B.set_uuid(uuidB);
|
||||||
|
name_B.set_id(2);
|
||||||
|
name_B.set_name("TestnameB");
|
||||||
|
|
||||||
|
|
||||||
|
QVERIFY2(utils.insertIntoTable(&name_A, "names").isActive(), "Successfull insert must return isActive sqlQuery");
|
||||||
|
QVERIFY2(utils.insertIntoTable(&name_B, "names").isActive(), "Successfull insert must return isActive sqlQuery");
|
||||||
|
|
||||||
|
|
||||||
|
// NOW UPDATING TABLE WITH name: NewName
|
||||||
|
name_A.set_name("TestnameA-new");
|
||||||
|
utils.updateTable(&name_A, "names");
|
||||||
|
name_B.set_name("TestnameB-new");
|
||||||
|
utils.updateTable(&name_B, "names");
|
||||||
|
|
||||||
|
// SELECT FROM TABLE
|
||||||
|
QSqlQuery selectSQL = db.execPlainSql("SELECT * FROM names");
|
||||||
|
auto list = utils.tableFromQuery<Name>(selectSQL);
|
||||||
|
QCOMPARE(list.count(), 2);
|
||||||
|
|
||||||
|
auto dbA = list.first();
|
||||||
|
auto dbB = list.last();
|
||||||
|
QCOMPARE(dbA->name(), name_A.name());
|
||||||
|
QCOMPARE(dbB->name(), name_B.name());
|
||||||
|
|
||||||
|
QCOMPARE(dbA->jsonData(), name_A.jsonData());
|
||||||
|
QCOMPARE(dbB->jsonData(), name_B.jsonData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** # Fail Update test
|
||||||
|
*
|
||||||
|
* Test isActive must be FALSE when not possible to update database table
|
||||||
|
*/
|
||||||
|
void SqliteTest::fail_updateTable_test()
|
||||||
|
{
|
||||||
|
// Prepare temporary path for database
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QString dbpath = dir.path() + "/testdb.sqlite";
|
||||||
|
serversdk::DatabaseManager db;
|
||||||
|
db.openSQLite(dbpath);
|
||||||
|
QVERIFY(db.isOpen());
|
||||||
|
|
||||||
|
Name name_A, name_B;
|
||||||
|
|
||||||
|
// CREATE TABLE
|
||||||
|
serversdk::DatabaseUtils utils(&db);
|
||||||
|
QSqlQuery query = utils.createTable(&name_A, "names");
|
||||||
|
|
||||||
|
// INSERT INTO TABLE
|
||||||
|
const QString uuidA = "48f64fab-ba2a-40b5-8fbb-75361099dfb6";
|
||||||
|
name_A.set_uuid(uuidA);
|
||||||
|
name_A.set_id(2);
|
||||||
|
name_A.set_name("TestnameA");
|
||||||
|
|
||||||
|
|
||||||
|
QVERIFY2(!utils.insertIntoTable(&name_A, "nonexistenttable").isActive(), "Successfull insert must return isActive sqlQuery");
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(SqliteTest)
|
||||||
|
|
||||||
|
#include "tst_sqlite.moc"
|
||||||
11
test/auto/database/tst_sqlite.pro
Normal file
11
test/auto/database/tst_sqlite.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_sqlite.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
@ -1,9 +1,16 @@
|
|||||||
#include "tst_request.h"
|
#include <QtTest>
|
||||||
|
|
||||||
#include "serversdk/request.h"
|
#include "serversdk/request.h"
|
||||||
|
|
||||||
namespace serversdk {
|
namespace serversdk {
|
||||||
|
|
||||||
|
class RequestTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
Q_SLOT void basicAuthorization_test();
|
||||||
|
};
|
||||||
|
|
||||||
void RequestTest::basicAuthorization_test()
|
void RequestTest::basicAuthorization_test()
|
||||||
{
|
{
|
||||||
QString expected = "Basic VGVzdGVyOlRlc3Q=";
|
QString expected = "Basic VGVzdGVyOlRlc3Q=";
|
||||||
@ -17,3 +24,5 @@ void RequestTest::basicAuthorization_test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(serversdk::RequestTest)
|
QTEST_APPLESS_MAIN(serversdk::RequestTest)
|
||||||
|
|
||||||
|
#include "tst_request.moc"
|
||||||
@ -3,7 +3,6 @@ CONFIG += testcase # neccesary to have posibility to run: make check UnitTest
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
tst_request.cpp
|
tst_request.cpp
|
||||||
HEADERS += \
|
|
||||||
tst_request.h
|
|
||||||
|
|
||||||
include(../../serversdkint.pri)
|
|
||||||
|
include(../../../serversdkint.pri)
|
||||||
140
test/auto/request/tst_requestmanager.cpp
Normal file
140
test/auto/request/tst_requestmanager.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "serversdk/requestmanager.h"
|
||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
|
||||||
|
#include "serversdk/core.h"
|
||||||
|
|
||||||
|
class LoginTable : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, username, set_username, "");
|
||||||
|
TABLE_PROPERTY(QString, password, set_password, "")
|
||||||
|
};
|
||||||
|
|
||||||
|
class Token : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QUuid, id, set_id, "");
|
||||||
|
public:
|
||||||
|
QString idWithoutCurlyBraces() {
|
||||||
|
return a_id.toString().mid(1, 36);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
class RequestManagerTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
RequestManagerTest() {
|
||||||
|
qRegisterMetaType<std::shared_ptr<serversdk::Request>>("std::shared_ptr<Request>");
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Q_SLOT void colnodAuthorization_test();
|
||||||
|
Q_SLOT void colnodGetUpdatedTokens_test();
|
||||||
|
|
||||||
|
Q_SLOT void notConncetion_test();
|
||||||
|
|
||||||
|
std::shared_ptr<Request> colnodLoginRequest();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<Request> RequestManagerTest::colnodLoginRequest()
|
||||||
|
{
|
||||||
|
// Colnod
|
||||||
|
auto login = std::make_unique<LoginTable>();
|
||||||
|
login->set_username("spaceti");
|
||||||
|
login->set_password("E7FEA07DBC82FB9BA34B2E453E2EFE89F0583E7187543507EF11E84726071FE428445372F2E46B455EB0C79747C51E362F403CE39473A2A8C2DFA0CE5C9826C0");
|
||||||
|
|
||||||
|
auto loginRequest = std::make_shared<serversdk::Request>("POST", "https://192.168.1.3:8443/AaaManager/authSession", login->jsonData());
|
||||||
|
loginRequest->setUserName(login->username());
|
||||||
|
loginRequest->setPassword(login->password());
|
||||||
|
return loginRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestManagerTest::colnodAuthorization_test()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
reqMan.setIgnoreSsl(true);
|
||||||
|
|
||||||
|
auto loginRequest = colnodLoginRequest();
|
||||||
|
QCOMPARE(loginRequest->basicAuthorization(), QStringLiteral("Basic c3BhY2V0aTpFN0ZFQTA3REJDODJGQjlCQTM0QjJFNDUzRTJFRkU4OUYwNTgzRTcxODc1NDM1MDdFRjExRTg0NzI2MDcxRkU0Mjg0NDUzNzJGMkU0NkI0NTVFQjBDNzk3NDdDNTFFMzYyRjQwM0NFMzk0NzNBMkE4QzJERkEwQ0U1Qzk4MjZDMA=="));
|
||||||
|
|
||||||
|
QSignalSpy loginFinished(loginRequest.get(), &serversdk::Request::finished);
|
||||||
|
QTest::ignoreMessage(QtDebugMsg, "\"SSL error:\" \"The host name did not match any of the valid hosts for this certificate\"");
|
||||||
|
reqMan.processRequest(loginRequest);
|
||||||
|
|
||||||
|
QVERIFY(loginFinished.wait());
|
||||||
|
QCOMPARE(loginFinished.count(), 1 );
|
||||||
|
|
||||||
|
|
||||||
|
auto data = loginRequest->responseData();
|
||||||
|
|
||||||
|
|
||||||
|
Token token;
|
||||||
|
QVERIFY(token.id().isNull()); // must be null
|
||||||
|
token.fromJson(data);
|
||||||
|
QVERIFY(!token.id().isNull()); // mut not be null ( authentication token )
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestManagerTest::colnodGetUpdatedTokens_test()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
reqMan.setIgnoreSsl(true);
|
||||||
|
|
||||||
|
auto loginRequest = colnodLoginRequest();
|
||||||
|
QCOMPARE(loginRequest->basicAuthorization(), QStringLiteral("Basic c3BhY2V0aTpFN0ZFQTA3REJDODJGQjlCQTM0QjJFNDUzRTJFRkU4OUYwNTgzRTcxODc1NDM1MDdFRjExRTg0NzI2MDcxRkU0Mjg0NDUzNzJGMkU0NkI0NTVFQjBDNzk3NDdDNTFFMzYyRjQwM0NFMzk0NzNBMkE4QzJERkEwQ0U1Qzk4MjZDMA=="));
|
||||||
|
|
||||||
|
QSignalSpy spyFinished(loginRequest.get(), &serversdk::Request::finished);
|
||||||
|
QTest::ignoreMessage(QtDebugMsg, "\"SSL error:\" \"The host name did not match any of the valid hosts for this certificate\"");
|
||||||
|
reqMan.processRequest(loginRequest);
|
||||||
|
|
||||||
|
QVERIFY(spyFinished.wait());
|
||||||
|
QCOMPARE(spyFinished.count(), 1 );
|
||||||
|
|
||||||
|
auto data = loginRequest->responseData();
|
||||||
|
|
||||||
|
Token token;
|
||||||
|
QVERIFY(token.id().isNull()); // must be null
|
||||||
|
token.fromJson(data);
|
||||||
|
QVERIFY(!token.id().isNull()); // mut not be null ( authentication token )
|
||||||
|
|
||||||
|
QByteArray array = R"({"timestamp": 1570109859681})";
|
||||||
|
|
||||||
|
auto tokensRequest = std::make_shared<serversdk::Request>("POST", "https://192.168.1.3:8443/TokenManager/getTokensUpdated", array);
|
||||||
|
tokensRequest->setUserName(token.idWithoutCurlyBraces());
|
||||||
|
QSignalSpy tokensFinished(tokensRequest.get(), &serversdk::Request::finished);
|
||||||
|
reqMan.processRequest(tokensRequest);
|
||||||
|
QVERIFY(tokensRequest->networkReply());
|
||||||
|
QVERIFY(tokensFinished.wait());
|
||||||
|
QCOMPARE(tokensFinished.count(), 1 );
|
||||||
|
|
||||||
|
QVERIFY(tokensRequest->status() == 200);
|
||||||
|
//qDebug() << tokensRequest->responseData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestManagerTest::notConncetion_test()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
reqMan.setIgnoreSsl(true);
|
||||||
|
|
||||||
|
auto tokensRequest = std::make_shared<serversdk::Request>("POST", "https://localhost:9999/TokenManager/getTokensUpdated", QByteArray("{}"));
|
||||||
|
//QTest::ignoreMessage(QtWarningMsg, QRegularExpression("POST.*error.*"));
|
||||||
|
reqMan.processRequest(tokensRequest);
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "POST https://localhost:9999/TokenManager/getTokensUpdated data: {} header: () reply error: QNetworkReply::NetworkError(ConnectionRefusedError)");
|
||||||
|
QVERIFY(tokensRequest->networkReply());
|
||||||
|
|
||||||
|
QSignalSpy tokensFinished(tokensRequest.get(), &serversdk::Request::finished);
|
||||||
|
QVERIFY(tokensFinished.wait());
|
||||||
|
QCOMPARE(tokensFinished.count(), 1 );
|
||||||
|
|
||||||
|
QCOMPARE(tokensRequest->status(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(serversdk::RequestManagerTest)
|
||||||
|
|
||||||
|
#include "tst_requestmanager.moc"
|
||||||
8
test/auto/request/tst_requestmanager.pro
Normal file
8
test/auto/request/tst_requestmanager.pro
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
QT += core sql network testlib
|
||||||
|
CONFIG += testcase # neccesary to have posibility to run: make check UnitTest
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
tst_requestmanager.cpp
|
||||||
|
|
||||||
|
|
||||||
|
include(../../../serversdkint.pri)
|
||||||
11
test/auto/request/tst_soap.pro
Normal file
11
test/auto/request/tst_soap.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_soaptest.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
219
test/auto/request/tst_soaptest.cpp
Normal file
219
test/auto/request/tst_soaptest.cpp
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include "serversdk/requestmanager.h"
|
||||||
|
#include "serversdk/utils.h"
|
||||||
|
#include "serversdk/soaprequest.h"
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace serversdk {
|
||||||
|
|
||||||
|
class SoapTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SoapTest();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_SLOT void construct();
|
||||||
|
|
||||||
|
/*Q_SLOT*/ void geocode_soap();
|
||||||
|
|
||||||
|
/*Q_SLOT*/ void public_soap_xml();
|
||||||
|
|
||||||
|
/*Q_SLOT*/ void public_soap_test();
|
||||||
|
Q_SLOT void public_soap_test_data();
|
||||||
|
};
|
||||||
|
|
||||||
|
SoapTest::SoapTest()
|
||||||
|
{
|
||||||
|
qRegisterMetaType<std::shared_ptr<Request>>("std::shared_ptr<Request>");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoapTest::construct()
|
||||||
|
{
|
||||||
|
RequestManager soap;
|
||||||
|
|
||||||
|
QVERIFY(soap.mRequestMap.isEmpty());
|
||||||
|
|
||||||
|
QFileInfo info(Test::sourcePath(""));
|
||||||
|
QCOMPARE(info.fileName(), QStringLiteral("data"));
|
||||||
|
|
||||||
|
QCOMPARE(Test::sourceData("soap/LatLonListSubgrid-response.xml").count(), 1298);
|
||||||
|
//QCOMPARE(Utils::sourceData("test/cmd/LatLonListSubgrid-response.xml").count(), 1298);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoapTest::geocode_soap()
|
||||||
|
{
|
||||||
|
const QString soapAction = "https://geoservices.tamu.edu/GeocodeAddressNonParsed";
|
||||||
|
const QString host = "https://geoservices.tamu.edu";
|
||||||
|
const QString endpoint = "/Services/Geocode/WebService/GeocoderService_V04_01.asmx";
|
||||||
|
const QString url = host + endpoint;
|
||||||
|
|
||||||
|
QString xml = QString(R"(<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
|
||||||
|
<soap12:Body>
|
||||||
|
<GeocodeAddressNonParsed xmlns="https://geoservices.tamu.edu/">
|
||||||
|
<streetAddress>string</streetAddress>
|
||||||
|
<city>string</city>
|
||||||
|
<state>string</state>
|
||||||
|
<zip>string</zip>
|
||||||
|
<apiKey>string</apiKey>
|
||||||
|
<version>double</version>
|
||||||
|
<shouldCalculateCensus>boolean</shouldCalculateCensus>
|
||||||
|
<censusYear>Unknown or NineteenNinety or TwoThousand or TwoThousandTen or AllAvailable</censusYear>
|
||||||
|
<shouldReturnReferenceGeometry>boolean</shouldReturnReferenceGeometry>
|
||||||
|
<shouldNotStoreTransactionDetails>boolean</shouldNotStoreTransactionDetails>
|
||||||
|
</GeocodeAddressNonParsed>
|
||||||
|
</soap12:Body>
|
||||||
|
</soap12:Envelope>)");
|
||||||
|
|
||||||
|
QByteArray xmlRequest = xml.toUtf8();
|
||||||
|
|
||||||
|
// SOAP download
|
||||||
|
RequestManager soap;
|
||||||
|
auto response = std::make_shared<SoapRequest>(soapAction, url, xmlRequest);
|
||||||
|
QCOMPARE(response.use_count(), 1L );
|
||||||
|
soap.processRequest(response);
|
||||||
|
QVERIFY(soap.mRequestMap.count() == 1);
|
||||||
|
QSignalSpy spyFinished(&soap, &RequestManager::downloadFinished);
|
||||||
|
|
||||||
|
QVERIFY(spyFinished.wait());
|
||||||
|
//QCOMPARE(response.use_count(), 2L ); // this context + Soap
|
||||||
|
spyFinished.clear();
|
||||||
|
QCOMPARE(response.use_count(), 1L ); // this context + Soap
|
||||||
|
|
||||||
|
// Response
|
||||||
|
QByteArray xmlResponse = Test::sourceData("soap/bankapi-response.xml");
|
||||||
|
QVERIFY( ! xmlResponse.isEmpty());
|
||||||
|
|
||||||
|
QCOMPARE(response.use_count(), 1L ); // this context + Soap
|
||||||
|
QCOMPARE(response->responseData(), xmlResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoapTest::public_soap_xml()
|
||||||
|
{
|
||||||
|
QString soapAction = "blz:getBank";
|
||||||
|
QString url = "http://www.thomas-bayer.com/axis2/services/BLZService";
|
||||||
|
QString xml = QString("<soapenv:Envelope "
|
||||||
|
"xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||||
|
"xmlns:blz=\"http://thomas-bayer.com/blz/\">"
|
||||||
|
"<soapenv:Header/>"
|
||||||
|
" <soapenv:Body>"
|
||||||
|
" <blz:getBank>"
|
||||||
|
" <blz:blz>%1</blz:blz>"
|
||||||
|
" </blz:getBank>"
|
||||||
|
"</soapenv:Body>"
|
||||||
|
"</soapenv:Envelope>").arg("10020200");
|
||||||
|
|
||||||
|
QByteArray xmlRequest = xml.toUtf8();
|
||||||
|
|
||||||
|
// SOAP download
|
||||||
|
RequestManager soap;
|
||||||
|
auto response = std::make_shared<SoapRequest>(soapAction, url, xmlRequest);
|
||||||
|
QCOMPARE(response.use_count(), 1L );
|
||||||
|
soap.processRequest(response);
|
||||||
|
QVERIFY(soap.mRequestMap.count() == 1);
|
||||||
|
QSignalSpy spyFinished(&soap, &RequestManager::downloadFinished);
|
||||||
|
|
||||||
|
QVERIFY(spyFinished.wait());
|
||||||
|
//QCOMPARE(response.use_count(), 2L ); // this context + Soap
|
||||||
|
spyFinished.clear();
|
||||||
|
QCOMPARE(response.use_count(), 1L ); // this context + Soap
|
||||||
|
|
||||||
|
// Response
|
||||||
|
QByteArray xmlResponse = Test::sourceData("soap/bankapi-response.xml");
|
||||||
|
QVERIFY( ! xmlResponse.isEmpty());
|
||||||
|
|
||||||
|
// QFile fileWrite(cmdDir() + "/tst_soap-response.xml");
|
||||||
|
// QVERIFY(fileWrite.open(QIODevice::WriteOnly));
|
||||||
|
// fileWrite.write(soap.data());
|
||||||
|
// fileWrite.close();
|
||||||
|
|
||||||
|
QCOMPARE(response.use_count(), 1L ); // this context + Soap
|
||||||
|
QCOMPARE(response->responseData(), xmlResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void SoapTest::public_soap_test()
|
||||||
|
{
|
||||||
|
QFETCH(QString, soapAction);
|
||||||
|
QFETCH(QString, endpoint);
|
||||||
|
QFETCH(QString, xmlRequestFile);
|
||||||
|
QFETCH(QString, xmlResponseFile);
|
||||||
|
QFETCH(bool, soap12);
|
||||||
|
|
||||||
|
// Request
|
||||||
|
QByteArray xmlRequest = Test::sourceData(xmlRequestFile);
|
||||||
|
QVERIFY( ! xmlRequest.isEmpty());
|
||||||
|
// Response
|
||||||
|
QByteArray xmlResponse = Test::sourceData(xmlResponseFile);
|
||||||
|
QVERIFY( ! xmlResponse.isEmpty());
|
||||||
|
|
||||||
|
// RequestManagerrequest
|
||||||
|
auto req = std::make_shared<SoapRequest>(soapAction, endpoint, xmlRequest);
|
||||||
|
req->setSoapVersion12(soap12);
|
||||||
|
|
||||||
|
// RequestManagerdownload
|
||||||
|
RequestManager soap;
|
||||||
|
soap.processRequest(req);
|
||||||
|
QCOMPARE(req->status(), 0); // when not processed yet, status is 0
|
||||||
|
|
||||||
|
QSignalSpy progressSpy(&soap, &RequestManager::progresss);
|
||||||
|
QSignalSpy finishedSpy(&soap, &RequestManager::downloadFinished);
|
||||||
|
QVERIFY(finishedSpy.wait(20000));
|
||||||
|
QVERIFY(progressSpy.count());
|
||||||
|
|
||||||
|
QCOMPARE(req->status(), 200);
|
||||||
|
|
||||||
|
//Uncoment to write .xml response to cmd folder
|
||||||
|
// QFile fileWrite(Utils::sourcePath("test/cmd/tst_soap-response.xml"));
|
||||||
|
// QVERIFY(fileWrite.open(QIODevice::WriteOnly));
|
||||||
|
// fileWrite.write(req->responseData());
|
||||||
|
// fileWrite.close();
|
||||||
|
// QProcess process;
|
||||||
|
// process.start("hostname");
|
||||||
|
// process.waitForFinished(-1); // will wait forever until finished
|
||||||
|
|
||||||
|
QCOMPARE(req->responseData(), xmlResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SoapTest::public_soap_test_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("soapAction");
|
||||||
|
QTest::addColumn<QString>("endpoint");
|
||||||
|
QTest::addColumn<QString>("xmlRequestFile");
|
||||||
|
QTest::addColumn<QString>("xmlResponseFile");
|
||||||
|
QTest::addColumn<bool>("soap12");
|
||||||
|
QTest::addColumn<bool>("soap12");
|
||||||
|
|
||||||
|
// QTest::newRow("weather service")
|
||||||
|
// << "LatLonListSubgrid"
|
||||||
|
// << "https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php"
|
||||||
|
// << "soap/LatLonListSubgrid.xml"
|
||||||
|
// << "soap/LatLonListSubgrid-response.xml"
|
||||||
|
// << false;
|
||||||
|
|
||||||
|
QTest::newRow("bank service")
|
||||||
|
<< "blz:getBank"
|
||||||
|
<< "http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||||
|
<< "soap/bankapi.xml"
|
||||||
|
<< "soap/bankapi-response.xml"
|
||||||
|
<< false;
|
||||||
|
|
||||||
|
QTest::newRow("bank service SOAP 1.2")
|
||||||
|
<< "blz:getBank"
|
||||||
|
<< "http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||||
|
<< "soap/bankapi-1.2.xml"
|
||||||
|
<< "soap/bankapi-1.2-response.xml"
|
||||||
|
<< true; // SOAP 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(serversdk::SoapTest)
|
||||||
|
|
||||||
|
#include "tst_soaptest.moc"
|
||||||
12
test/auto/tst_utils/tst_utils.pro
Normal file
12
test/auto/tst_utils/tst_utils.pro
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_utilstest.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
|
|
||||||
37
test/auto/tst_utils/tst_utilstest.cpp
Normal file
37
test/auto/tst_utils/tst_utilstest.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include "serversdk/utils.h"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
class UtilsTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
UtilsTest() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_SLOT void decompresTest();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void UtilsTest::decompresTest()
|
||||||
|
{
|
||||||
|
QByteArray expectedData = Test::sourceData("gzip.src");
|
||||||
|
int expectedSize = 895;
|
||||||
|
QCOMPARE( expectedData.count(), expectedSize);
|
||||||
|
|
||||||
|
QByteArray compressedData = Test::sourceData("gzip.compressed");
|
||||||
|
int compressedSize = 411;
|
||||||
|
QCOMPARE( compressedData.count(), compressedSize);
|
||||||
|
|
||||||
|
QByteArray decompressedData = serversdk::Utils::decompress(compressedData);
|
||||||
|
|
||||||
|
QCOMPARE(decompressedData.count(), expectedSize);
|
||||||
|
QCOMPARE(decompressedData, expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(UtilsTest)
|
||||||
|
|
||||||
|
#include "tst_utilstest.moc"
|
||||||
BIN
test/data/gzip.compressed
Normal file
BIN
test/data/gzip.compressed
Normal file
Binary file not shown.
15
test/data/gzip.src
Normal file
15
test/data/gzip.src
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Příliš žluťoušký kůň
|
||||||
|
compresed using: gzip -c gzip.src > gzip.compressed
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<SOAP-ENV:Body>
|
||||||
|
<ns5689:LatLonListSubgrid xmlns:ns5689="uri:DWMLgen">
|
||||||
|
<lowerLeftLatitude xsi:type="xsd:string">35.00</lowerLeftLatitude>
|
||||||
|
<lowerLeftLongitude xsi:type="xsd:string">-82.00</lowerLeftLongitude>
|
||||||
|
<upperRightLatitude xsi:type="xsd:string">35.5</upperRightLatitude>
|
||||||
|
<upperRightLongitude xsi:type="xsd:string">-81.50</upperRightLongitude>
|
||||||
|
<resolution xsi:type="xsd:string">20.0</resolution>
|
||||||
|
</ns5689:LatLonListSubgrid>
|
||||||
|
</SOAP-ENV:Body>
|
||||||
|
</SOAP-ENV:Envelope>
|
||||||
1
test/data/soap/LatLonListSubgrid-response.xml
Normal file
1
test/data/soap/LatLonListSubgrid-response.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:LatLonListSubgridResponse xmlns:ns1="uri:DWMLgen"><listLatLonOut xsi:type="xsd:string"><?xml version='1.0'?><dwml version='1.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='https://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd'><latLonList>34.986638,-82.027411 35.120891,-82.011661 35.255084,-81.995872 35.389215,-81.980045 35.523285,-81.964180 34.973669,-81.863528 35.107911,-81.847581 35.242094,-81.831596 35.376216,-81.815571 35.510276,-81.799508 34.960536,-81.699684 35.094768,-81.683540 35.228941,-81.667358 35.363052,-81.651136 35.497102,-81.634876 34.947240,-81.535878 35.081463,-81.519539 35.215625,-81.503160 35.349726,-81.486741 35.483766,-81.470282</latLonList></dwml></listLatLonOut></ns1:LatLonListSubgridResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
|
||||||
12
test/data/soap/LatLonListSubgrid.xml
Normal file
12
test/data/soap/LatLonListSubgrid.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<SOAP-ENV:Body>
|
||||||
|
<ns5689:LatLonListSubgrid xmlns:ns5689="uri:DWMLgen">
|
||||||
|
<lowerLeftLatitude xsi:type="xsd:string">35.00</lowerLeftLatitude>
|
||||||
|
<lowerLeftLongitude xsi:type="xsd:string">-82.00</lowerLeftLongitude>
|
||||||
|
<upperRightLatitude xsi:type="xsd:string">35.5</upperRightLatitude>
|
||||||
|
<upperRightLongitude xsi:type="xsd:string">-81.50</upperRightLongitude>
|
||||||
|
<resolution xsi:type="xsd:string">20.0</resolution>
|
||||||
|
</ns5689:LatLonListSubgrid>
|
||||||
|
</SOAP-ENV:Body>
|
||||||
|
</SOAP-ENV:Envelope>
|
||||||
0
test/data/soap/bankapi-1.2-response.xml
Normal file
0
test/data/soap/bankapi-1.2-response.xml
Normal file
14
test/data/soap/bankapi-1.2.sh
Executable file
14
test/data/soap/bankapi-1.2.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# @see https://stackoverflow.com/a/55352633/1917249
|
||||||
|
|
||||||
|
ACTION_YOU_WANT_TO_CALL="blz:getBank"
|
||||||
|
|
||||||
|
XMLFILENAME=bankapi-1.2.xml
|
||||||
|
|
||||||
|
URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT="http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||||
|
|
||||||
|
curl -X POST $URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT \
|
||||||
|
-H 'Content-Type: application/soap+xml;action=$ACTION_YOU_WANT_TO_CALL' \
|
||||||
|
-H 'SOAPAction: $ACTION_YOU_WANT_TO_CALL' \
|
||||||
|
--data @$XMLFILENAME > bankapi-1.2-response.xml
|
||||||
10
test/data/soap/bankapi-1.2.xml
Normal file
10
test/data/soap/bankapi-1.2.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<soapenv:Envelope
|
||||||
|
xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"
|
||||||
|
xmlns:blz="http://thomas-bayer.com/blz/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<blz:getBank>
|
||||||
|
<blz:blz>10020200</blz:blz>
|
||||||
|
</blz:getBank>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
1
test/data/soap/bankapi-response.xml
Normal file
1
test/data/soap/bankapi-response.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><ns1:getBankResponse xmlns:ns1="http://thomas-bayer.com/blz/"><ns1:details><ns1:bezeichnung>BHF-BANK</ns1:bezeichnung><ns1:bic>BHFBDEFF100</ns1:bic><ns1:ort>Berlin</ns1:ort><ns1:plz>10117</ns1:plz></ns1:details></ns1:getBankResponse></soapenv:Body></soapenv:Envelope>
|
||||||
14
test/data/soap/bankapi.sh
Executable file
14
test/data/soap/bankapi.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# @see https://stackoverflow.com/a/55352633/1917249
|
||||||
|
|
||||||
|
ACTION_YOU_WANT_TO_CALL="blz:getBank"
|
||||||
|
|
||||||
|
XMLFILENAME=bankapi.xml
|
||||||
|
|
||||||
|
URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT="http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||||
|
|
||||||
|
curl -X POST $URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT \
|
||||||
|
-H 'Content-Type: text/xml' \
|
||||||
|
-H 'SOAPAction: $ACTION_YOU_WANT_TO_CALL' \
|
||||||
|
--data @$XMLFILENAME > bankapi-response.xml
|
||||||
10
test/data/soap/bankapi.xml
Normal file
10
test/data/soap/bankapi.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<soapenv:Envelope
|
||||||
|
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:blz="http://thomas-bayer.com/blz/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<blz:getBank>
|
||||||
|
<blz:blz>10020200</blz:blz>
|
||||||
|
</blz:getBank>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
34
test/test.cpp
Normal file
34
test/test.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return path to source directory
|
||||||
|
* @param subpath ( e.g. test/data )
|
||||||
|
* @return absolute path to desired subpath in source folder
|
||||||
|
*/
|
||||||
|
QString Test::sourcePath(const QString &subpath)
|
||||||
|
{
|
||||||
|
const QString path = QStringLiteral(SDK_TEST_PATH);
|
||||||
|
QDir dir(path);
|
||||||
|
return dir.absoluteFilePath(subpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return data from source directory
|
||||||
|
* @param subpath ( e.g. test/data/gizp.src )
|
||||||
|
* @return data of file - QByteArray
|
||||||
|
*/
|
||||||
|
QByteArray Test::sourceData(const QString &subpath)
|
||||||
|
{
|
||||||
|
// Get data from file
|
||||||
|
QString filePath = sourcePath(subpath);
|
||||||
|
if (! QFileInfo::exists(filePath)) {
|
||||||
|
qWarning() << "path does not exists:" << filePath;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
QFile file(filePath);
|
||||||
|
if ( file.open(QIODevice::ReadOnly) )
|
||||||
|
return file.readAll();
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
@ -1,19 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
namespace core {
|
class Test
|
||||||
|
|
||||||
class Compression
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static QByteArray decompress(const QByteArray &compressed);
|
|
||||||
|
|
||||||
static QString sourcePath(const QString &subpath);
|
static QString sourcePath(const QString &subpath);
|
||||||
static QByteArray sourceData(const QString &subpath);
|
static QByteArray sourceData(const QString &subpath);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
|
|
||||||
17
test/test.pri
Normal file
17
test/test.pri
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
include(../serversdkint.pri)
|
||||||
|
|
||||||
|
SDK_ROOT=$$PWD/..
|
||||||
|
SDK_TEST_PATH=$$SDK_ROOT/test/data
|
||||||
|
|
||||||
|
DEFINES += SDK_ROOT='\\"$$SDK_ROOT\\"'
|
||||||
|
DEFINES += SDK_TEST_PATH='\\"$$SDK_TEST_PATH\\"'
|
||||||
|
|
||||||
|
QT += testlib
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/test.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/test.cpp
|
||||||
@ -1,5 +1,5 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
unittests
|
auto \
|
||||||
|
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtTest/QtTest>
|
|
||||||
|
|
||||||
namespace serversdk {
|
|
||||||
|
|
||||||
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 basicAuthorization_test();
|
|
||||||
};
|
|
||||||
|
|
||||||
} //
|
|
||||||
Loading…
x
Reference in New Issue
Block a user