Compare commits

...

108 Commits

Author SHA1 Message Date
Ondřej Fischer
4a2df4661e Not using zlib 2020-10-20 17:16:04 +02:00
Ondřej Fischer
b1897b6a74 add get function for request process time 2020-09-16 12:33:12 +02:00
Filip Bucek
b54f2b332e status should return http status ( -1 when error, 0 not started ) 2020-02-18 14:50:57 +01:00
Filip Bucek
396c046afc removed suppressing message ( no message on linux ) 2020-02-18 14:47:28 +01:00
Filip Bucek
aff09b92da disabled soap test ( service unavailable )
* renamed files in script based on their names
2020-02-18 14:44:01 +01:00
Filip Bucek
fdfe31e064 fix: wrong finish time in request handling
* fixed signal warning
* suppressing SSL messages in tests
2020-02-18 13:49:36 +01:00
Filip Bucek
d4c4d75b49 Merge branch 'edit-cppcheck' into 'master'
Edit cppcheck

See merge request spaceti-app/integrations/common/server-sdk!18
2019-12-16 09:28:20 +00:00
Filip Bucek
2b12a67f52 fixed typo: missing p in suppress variable 2019-12-16 10:25:36 +01:00
Filip Bucek
46829870f0 ensuring server-sdk have unusedFunction suppresion 2019-12-16 10:19:48 +01:00
Ondřej Fischer
a555fd93e2 delete suppress unused function 2019-12-16 10:05:36 +01:00
Filip Bucek
19f748cc73 Merge branch 'improve/make-property-const-ref' into 'master'
const ref in table_property set function

See merge request spaceti-app/integrations/common/server-sdk!17
2019-12-12 10:19:26 +00:00
Ondřej Fischer
28a61d7c12 const ref in table_property 2019-12-12 11:12:37 +01:00
Filip Bucek
3640c0db5d Merge branch 'feat/process-request' into 'master'
ability to check whether networkreply was really created ( rare test )

See merge request spaceti-app/integrations/common/server-sdk!16
2019-12-05 08:51:20 +00:00
Filip Bucek
9088d93a4e ability to check whether networkreply was really created ( rare test ) 2019-12-05 09:48:10 +01:00
Filip Bucek
b643f25421 Merge branch 'upgrade/add-filter-to-update-table' into 'master'
add fillter to update

See merge request spaceti-app/integrations/common/server-sdk!15
2019-12-03 09:19:34 +00:00
Ondřej Fischer
90541c3421 add fillter to update 2019-12-02 18:13:17 +01:00
Filip Bucek
031b6c2535 Merge branch 'fix/clang-checks' into 'master'
clang: fixing warnings without impact on function

See merge request spaceti-app/integrations/common/server-sdk!14
2019-12-02 09:05:33 +00:00
Filip Bucek
e4673cf66a clang: fix warning + making TABLE_PROPERTY private 2019-12-02 10:02:09 +01:00
Filip Bucek
ec406abc6f clang: fixing warnings without impact on function 2019-12-02 09:55:59 +01:00
Filip Bucek
a818055b75 Merge branch 'fix/changed-applogger-message' into 'master'
changed info message for applogger to debug

See merge request spaceti-app/integrations/common/server-sdk!13
2019-11-28 11:00:58 +00:00
Filip Bucek
dbe8aa5775 changed info message for applogger to debug 2019-11-28 11:53:33 +01:00
Filip Bucek
744bc7a785 Merge branch 'fix/codecovere-filter' into 'master'
fix: not filtering out serversdk

See merge request spaceti-app/integrations/common/server-sdk!12
2019-11-28 10:52:50 +00:00
Filip Bucek
924cfbde0f Merge branch 'temp/functionality-because-of-colnod-(test)' into 'master'
send request in finish signal

See merge request spaceti-app/integrations/common/server-sdk!11
2019-11-28 10:15:46 +00:00
Ondřej Fischer
2041fed9fa send request in finish signal 2019-11-28 10:47:33 +01:00
Filip Bucek
5ee82764e7 fix: not filtering out serversdk 2019-11-27 16:38:29 +01:00
Filip Bucek
61feac4632 removed Q_ASSERT for validation opened database
When error database contains information about url etc.
2019-11-27 14:16:09 +01:00
Filip Bucek
ee08677641 Merge branch 'feat/adding-connection-timetout' into 'master'
adding timeout to postgresql connection

See merge request spaceti-app/integrations/common/server-sdk!10
2019-11-27 11:37:13 +00:00
Filip Bucek
2e574f7b49 adding timeout to postgresql connection 2019-11-27 12:35:32 +01:00
Filip Bucek
9623578997 fix missing const 2019-11-25 16:28:08 +01:00
Filip Bucek
99089d1b12 emitting error when SQL error 2019-11-25 16:24:33 +01:00
Filip Bucek
eddfc401b0 request manager: outputting error 2019-11-25 12:58:15 +01:00
Filip Bucek
9fc60947eb Merge branch 'feat/logger' into 'master'
Feat/logger

See merge request spaceti-app/integrations/common/server-sdk!9
2019-11-25 08:20:33 +00:00
Filip Bucek
38a7da300d disabled color output for Windows 2019-11-25 08:46:59 +01:00
Filip Bucek
5025a68891 WIP: better logger output 2019-11-20 17:57:57 +01:00
Filip Bucek
9dd13d2970 Merge branch 'filtered-methods' into 'master'
Filtered methods

See merge request spaceti-app/integrations/common/server-sdk!8
2019-11-19 13:18:47 +00:00
Filip Bucek
f387b600f7 DatabaseTable new method primaryKey 2019-11-19 14:15:40 +01:00
Filip Bucek
c038568124 adding filtered methods 2019-11-19 13:53:05 +01:00
Filip Bucek
7f369b075d unitest -> filteredJson ( maybe make universal ) 2019-11-19 13:40:19 +01:00
Filip Bucek
1cf415820f Added unitest as example where baseclas does not have property of derived class 2019-11-19 11:43:00 +01:00
Filip Bucek
956f51e3e2 Merge branch 'update-unittest' into 'master'
Update unittest

See merge request spaceti-app/integrations/common/server-sdk!7
2019-11-19 09:25:59 +00:00
Filip Bucek
9cc150d50b fixing unittest ( update unittest, primary key, camelCase ) 2019-11-19 10:17:22 +01:00
Filip Bucek
e266b8a8b4 Not using Quid 2019-11-19 09:40:40 +01:00
Ondřej Fischer
583a652cff add create query 2019-11-19 09:15:45 +01:00
Ondřej Fischer
7071d874b0 fix error caused by using toCamelCase 2019-11-18 17:06:49 +01:00
Filip Bucek
793bfde1de Example in tst_databasetable how to handle different Json / Table data
+ camel case handling
2019-11-18 10:41:24 +01:00
Filip Bucek
4dfd4227cb Merge branch 'refs/heads/master' into possible-change
# Conflicts:
#	src/serversdk/databaseutils.h
2019-11-14 14:56:13 +01:00
Ondřej Fischer
34d112062c add function for UPDATE, needs to be edited 2019-11-14 14:46:44 +01:00
Filip Bucek
ad68c85086 fixing previous build: now using DatabaseUtils to get data from table
QSqlQuery selectSQL = db.execPlainSql("SELECT * FROM names");
auto list = utils.tableFromQuery<Names>(selectSQL);
2019-11-14 14:37:47 +01:00
Filip Bucek
8821dfc9b2 removed TABLE_PROPERTY form default class
+ had to subclass
+ removed unused code
2019-11-14 13:33:17 +01:00
Filip Bucek
29f7103ece Merge branch 'fix-collision-with-global-variable' into 'master'
stdout colidate with special variable for Standard output

See merge request spaceti-app/integrations/common/server-sdk!5
2019-11-14 12:18:59 +00:00
Ondřej Fischer
22d45162d4 stdout colidate with special variable for Standard output 2019-11-14 13:09:16 +01:00
Filip Bucek
5ea2b6f3e1 ZLIB enabled only for UNIX based systems 2019-11-14 12:51:28 +01:00
Filip Bucek
886cc3c777 added comment about subclassing DataTable 2019-11-14 10:54:02 +01:00
Filip Bucek
dfe6d3bb03 fix isSelect ( QSqlQuery::isSelect() does not work as expected ) 2019-11-13 14:56:22 +01:00
Filip Bucek
f1d78192f7 Adding test for colnod -> TokenManager -> getTokenUpdated 2019-11-13 10:56:31 +01:00
Filip Bucek
6940cad69e update network request 2019-11-13 10:19:44 +01:00
Filip Bucek
3f3af6cfa6 ci: disabling cache 2019-11-13 08:20:38 +01:00
Filip Bucek
549ba2abe2 fixing problem with build 2019-11-13 08:17:15 +01:00
Filip Bucek
4983bbb782 enabling debug for unittesting 2019-11-13 08:13:32 +01:00
Filip Bucek
b92095a459 SQL type based command move to DatabaseTable 2019-11-13 07:51:15 +01:00
Filip Bucek
16fe2db973 added support for Postgresql database 2019-11-12 17:42:54 +01:00
Filip Bucek
c4c51e5099 updated comments to DatabaseTable 2019-11-12 16:59:41 +01:00
Filip Bucek
136ee8300a added missing <memory> header file 2019-11-12 16:29:12 +01:00
Filip Bucek
493d82896b added missing <memory> header file 2019-11-12 16:22:42 +01:00
Filip Bucek
eb2a6c92fa Possible to INSERT and SELECT from database 2019-11-12 16:16:39 +01:00
Filip Bucek
174ede92ee fixing unittests ( using guiless app ) 2019-11-12 14:13:39 +01:00
Filip Bucek
30925777b4 DatabaseTable can read and write data to JSON + manager can ignore ssl errors 2019-11-12 13:11:30 +01:00
Filip Bucek
a80552654b added status() and networkReply() to Request 2019-11-12 12:02:50 +01:00
Filip Bucek
a7831cae9c updated json handling in databasetable 2019-11-12 11:37:37 +01:00
Filip Bucek
08f7f163d2 added core.h ( which includes <memory> ) 2019-11-12 11:27:06 +01:00
Filip Bucek
d467feb7b9 SQLite with create and insert based on defined struct 2019-11-12 11:25:01 +01:00
Filip Bucek
995898c00c cppcheck: explicit constructor 2019-11-11 18:09:44 +01:00
Filip Bucek
1cd286dba5 added missing <memory> header 2019-11-11 18:07:34 +01:00
Filip Bucek
d98ddf6c5f Adding database Table class for automated handling tables 2019-11-11 18:04:56 +01:00
Filip Bucek
b04332059f Adding SQLite test ( open database ) 2019-11-11 15:39:25 +01:00
Filip Bucek
70e389ab43 Using tst_ in auto tests 2019-11-11 13:12:48 +01:00
Filip Bucek
d54da50d5e moved SDK_TEST_PATH and SDK_ROOT definition to test subproject 2019-11-11 10:34:49 +01:00
Filip Bucek
54d7a5c59a used quoted path ( spaces in paths on Windows ) 2019-11-11 09:56:25 +01:00
Filip Bucek
1fc722b751 adding ansible ( to create test database ) 2019-11-11 09:29:42 +01:00
Filip Bucek
8552cc304f updating ci 2019-11-08 18:33:38 +01:00
Filip Bucek
1d947e931b updating ci 2019-11-08 18:28:06 +01:00
Filip Bucek
33950c96fe updating code coverage 2019-11-08 18:19:23 +01:00
Filip Bucek
ee507683c6 updating ci 2019-11-08 17:58:17 +01:00
Filip Bucek
4ded480088 using artifact with dependancy 2019-11-08 17:51:32 +01:00
Filip Bucek
22e281a2a7 using again 'cache' 2019-11-08 17:42:01 +01:00
Filip Bucek
00596d53db using make check 2019-11-08 17:13:59 +01:00
Filip Bucek
8f2fcedeb7 updating code coverage test 2019-11-08 16:36:46 +01:00
Filip Bucek
5618a7d00c updating tests 2019-11-08 16:28:15 +01:00
Filip Bucek
ce9e59d604 adding dependancy 2019-11-08 16:22:30 +01:00
Filip Bucek
01bd3c9753 adding dependancy 2019-11-08 16:21:53 +01:00
Filip Bucek
0d4f436e23 using artifacts instead of cache 2019-11-08 16:10:15 +01:00
Filip Bucek
1fe9632720 Merge branch 'feature/soap-test' into 'master'
Feature/soap test

See merge request spaceti-app/integrations/common/server-sdk!3
2019-11-08 14:46:45 +00:00
Filip Bucek
8c137c391f updated comments 2019-11-08 15:43:56 +01:00
Filip Bucek
0e2922f673 disabled http/2 - does not work on Qt 5.9.8 also for SoapRequest 2019-11-08 14:13:35 +01:00
Filip Bucek
1c2d69786c disabled http/2 - does not work on Qt 5.9.8 2019-11-08 14:11:17 +01:00
Filip Bucek
aeb98c0f06 updated unitests 2019-11-08 14:08:58 +01:00
Filip Bucek
ea0258cef1 Merge branch 'master' into feature/soap-test
# Conflicts:
#	scripts/check.sh
2019-11-08 13:32:59 +01:00
Filip Bucek
ea042620b1 SOAP is working with tests 2019-11-08 13:32:34 +01:00
Filip Bucek
b6950b9bfe fix lineending checks for .git 2019-11-08 11:26:09 +01:00
Filip Bucek
57e0245b1b fixing check script for lineendings 2019-11-08 11:15:34 +01:00
Filip Bucek
27a533d52b fixing check script for lineendings 2019-11-08 11:14:59 +01:00
Filip Bucek
c24bfa5296 Merge branch 'fix/line-endings' into 'master'
Fix/line endings

See merge request spaceti-app/integrations/common/server-sdk!2
2019-11-08 10:05:44 +00:00
Filip Bucek
02caeb86c9 lineending: fixed 2019-11-08 11:01:30 +01:00
Filip Bucek
0cf21cf7c2 WIP: fixed missing check ( lineendings ) 2019-11-08 10:56:22 +01:00
Filip Bucek
cb61286f33 WIP: check: lineending -> fixing 2019-11-08 10:55:20 +01:00
Filip Bucek
e650163631 Merge branch 'feat/compression' into 'master'
added compression with tests

See merge request spaceti-app/integrations/common/server-sdk!1
2019-11-08 08:34:43 +00:00
Filip Bucek
6909ae182c added compression with tests 2019-11-08 09:31:46 +01:00
Filip Bucek
0c7887d944 removing unnecessary code 2019-11-08 08:06:56 +01:00
69 changed files with 3019 additions and 773 deletions

View File

@ -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
@ -12,17 +20,14 @@ build: # name of task
tags: # Tags are used to choose correct gitlab-runner tags: # Tags are used to choose correct gitlab-runner
- shell # spaceti-runner server has running runners and one has tag 'hw' - shell # spaceti-runner server has running runners and one has tag 'hw'
script: # run in bash script: # run in bash
- 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
View 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
View 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`

View File

@ -0,0 +1,8 @@
- hosts: db
become: yes
vars_files:
- postgresql/vars.yml
vars:
roles:
- geerlingguy.postgresql

10
ansible/inventory.yml Normal file
View 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
View 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

View 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"

View File

@ -2,3 +2,33 @@
[![pipeline status](https://gitlab.com/spaceti-app/integrations/common/server-sdk/badges/master/pipeline.svg)](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master) [![pipeline status](https://gitlab.com/spaceti-app/integrations/common/server-sdk/badges/master/pipeline.svg)](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master)
[![coverage report](https://gitlab.com/spaceti-app/integrations/common/server-sdk/badges/master/coverage.svg)](https://gitlab.com/spaceti-app/integrations/common/server-sdk/commits/master) [![coverage report](https://gitlab.com/spaceti-app/integrations/common/server-sdk/badges/master/coverage.svg)](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
View 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

View File

@ -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
View File

View 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

View File

@ -14,7 +14,5 @@ OTHER_FILES += \
scripts/* \ scripts/* \
doc/Doxy* \ doc/Doxy* \
doc/pages/* \ doc/pages/* \
serversdk.pri \ *.pri \
serversdkint.pri \
message($$OUT_ROOT)

View File

@ -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

View File

@ -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

View File

@ -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";
} }

View File

@ -1,56 +1,172 @@
#include "databasemanager.h" #include "databasemanager.h"
#include "logger.h"
namespace serversdk {
DatabaseManager::DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool dryMode)
: mDryMode(dryMode)
{ namespace serversdk {
db = QSqlDatabase::addDatabase("QPSQL");
db.setDatabaseName(dbName); DatabaseManager::DatabaseManager()
db.setHostName(host); {
db.setUserName(user); // home-office stuff -->> db.setPort(80); }
db.setPassword(password);
if (db.open())
{
query = QSqlQuery("query", db); DatabaseManager::~DatabaseManager()
} else { {
log("Opening error" + db.lastError().text()); if (mDatabase.isValid()) {
emit error(); mDatabase.close();
} mDatabase = QSqlDatabase(); // invalidate database
} qDebug(sdkdb) << "removing database connection:" << mConnectionName;
QSqlDatabase::removeDatabase(mConnectionName);
bool DatabaseManager::executeQuery(QSqlQuery &query) }
{ }
if (mDryMode){
QString sqlStatement = getLastQuery(query); /**
qDebug() << sqlStatement; * @brief Creates new SQLite connection
* @param path to database ( will be created when none )
} * @param connectionName
if (!mDryMode || query.isSelect()){ * @return
if (!query.exec()){ */
QString msg = QString("Source: Database Manager, Error: %1").arg(query.lastError().text()); bool DatabaseManager::openSQLite(const QString &path, const QString &connectionName)
log(QString(msg)); {
emit error(); mConnectionName = connectionName;
} mDatabase = QSqlDatabase::addDatabase("QSQLITE", mConnectionName);
} mDatabase.setDatabaseName(path);
return true;
} int res = mDatabase.open();
QString DatabaseManager::getLastQuery(const QSqlQuery& query) const if (!res) {
{ qInfo(sdkdb) << "Not possible to open database from path:" << path;
QString str = query.lastQuery(); }
QMapIterator<QString, QVariant> it(query.boundValues()); return res;
while (it.hasNext()) }
{
it.next(); bool DatabaseManager::openPostgres(const QString &host, const QString &dbName, const QString &user, const QString &password, const QString &connectionName)
str.replace(it.key(),it.value().toString()); {
} Q_ASSERT_X(mConnectionName.isEmpty(), "open postgres", "Expecting only one database per DatabaseManager");
return str; mConnectionName = connectionName;
} mDatabase = QSqlDatabase::addDatabase("QPSQL", mConnectionName);
void DatabaseManager::log(const QString &msg) const mDatabase.setDatabaseName(dbName);
{ mDatabase.setHostName(host);
qDebug() << "database logger implement logger"; mDatabase.setUserName(user); // home-office stuff -->> db.setPort(80);
} mDatabase.setPassword(password);
// @see https://www.qtcentre.org/threads/39913-QSqlDatabase-connection-timeout
} // core 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) {
qCInfo(sdkdb) << "SQL statement: " << 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);
}
res = query.exec();
return processResult(res, query);
}
return query;
}
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 str = query.lastQuery();
QMapIterator<QString, QVariant> it(query.boundValues());
while (it.hasNext())
{
it.next();
str.replace(it.key(),it.value().toString());
}
return str;
}
void DatabaseManager::log(const QString &msg) const
{
qDebug() << "database logger implement logger";
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

View File

@ -1,37 +1,57 @@
#pragma once #pragma once
#include <QSql> #include <QSql>
#include <QSqlDatabase> #include <QSqlDatabase>
#include <QSqlQuery> #include <QSqlQuery>
#include <QSqlError> #include <QSqlError>
#include <QVariant> #include <QVariant>
#include <QDebug> #include <QDebug>
#include <QDateTime> #include <QDateTime>
//#include "logger.h" //#include "logger.h"
namespace serversdk { namespace serversdk {
class DatabaseManager : public QObject class DatabaseManager : public QObject
{ {
Q_OBJECT Q_OBJECT
friend class Logger; friend class Logger;
public: public:
DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool mDryMode = false); DatabaseManager();
DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, bool mDryMode = false);
private: ~DatabaseManager() override;
bool executeQuery(QSqlQuery &query);
QString getLastQuery(const QSqlQuery &query) const; // Attributes
bool dryMode() const;
void log(const QString &msg) const; void setDryMode(bool dryMode);
signals: bool openSQLite(const QString &path, const QString &connectionName = QStringLiteral("qt_sql_default_connection"));
void error(); 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;
private: bool isOpen() const;
QSqlDatabase db; void close();
QSqlQuery query;
bool mDryMode = false; // SQL handling
}; QSqlQuery execPlainSql(const QString &sqlText);
QSqlQuery executeQuery(QSqlQuery &query, const QString &sqlText = QString());
} // core QSqlQuery createQuery();
QSqlDatabase database() const;
private:
QSqlQuery processResult(int res, const QSqlQuery &query) const;
QString getLastQuery(const QSqlQuery &query) const;
void log(const QString &msg) const;
signals:
void sqlError(const QSqlQuery &query) const;
private:
QString mConnectionName;
QSqlDatabase mDatabase;
QSqlQuery query;
bool mDryMode = false;
};
} // core

View 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

View 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

View 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

View 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

View File

@ -1,103 +1,132 @@
#include "logger.h" #include "logger.h"
#include "loggermessage.h" #include "loggermessage.h"
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QFileInfo> #include <QFileInfo>
#include <QDateTime>
Q_LOGGING_CATEGORY(sdkcore, "sdk.core");
Q_LOGGING_CATEGORY(sdknetwork, "sdk.network"); Q_LOGGING_CATEGORY(sdkcore, "sdk.core");
Q_LOGGING_CATEGORY(sdkcompress, "sdk.compress"); Q_LOGGING_CATEGORY(sdknetwork, "sdk.network");
Q_LOGGING_CATEGORY(sdkutils, "sdk.utils");
namespace serversdk { Q_LOGGING_CATEGORY(sdkutilsdb, "sdk.utils.db");
Q_LOGGING_CATEGORY(sdkdb, "sdk.db");
Logger* Logger::sConsumer = nullptr;
//bool Logger::sShowFunction = false; namespace serversdk {
Logger* Logger::sConsumer = nullptr;
void Logger::setConsumer(Logger *consumer) #ifdef Q_OS_UNIX
{ bool Logger::mColorOutput = true;
sConsumer = consumer; #else
} bool Logger::mColorOutput = false;
#endif
Logger::Logger(QObject *parent) : QObject(parent)
{
qRegisterMetaType<LoggerMessage>(); void Logger::setConsumer(Logger *consumer)
} {
sConsumer = consumer;
QList<LoggerMessage> Logger::getNewMessages(int currentCount) }
{
QMutexLocker locker(&mMutex); Logger::Logger(QObject *parent) : QObject(parent)
QList<LoggerMessage> list; {
qRegisterMetaType<LoggerMessage>();
int count = mLogMessages.count();
int total = count - currentCount; }
if (total <= 0)
return list; QList<LoggerMessage> Logger::getNewMessages(int currentCount)
{
for (int i = 0; i < total; ++i) { QMutexLocker locker(&mMutex);
int currentIndex = currentCount; QList<LoggerMessage> list;
int row = i + currentIndex;
list.append(mLogMessages.at(row)); int count = mLogMessages.count();
} int total = count - currentCount;
return list; if (total <= 0)
} return list;
for (int i = 0; i < total; ++i) {
void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg) int currentIndex = currentCount;
{ int row = i + currentIndex;
QString shortFile = QFileInfo(context.file).fileName(); list.append(mLogMessages.at(row));
}
QString text; return list;
QString textType; }
switch (type) { QString Logger::toColor(const QString &color, const QString &text)
case QtDebugMsg: {
textType = QStringLiteral("debug"); return color + text + "\033[0m";
break; }
case QtWarningMsg:
textType = QStringLiteral("warning"); void Logger::setColorOutput(bool colorOutput)
break; {
case QtCriticalMsg: mColorOutput = colorOutput;
textType = QStringLiteral("critical"); }
break;
case QtFatalMsg:
textType = QStringLiteral("fatal"); void Logger::outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
break; {
#if QT_VERSION >= 0x050500 QString shortFile = QFileInfo(context.file).fileName();
case QtInfoMsg:
textType = QStringLiteral("info"); QString text;
break; QString textType;
#endif QString colorTextType;
}
if (sConsumer) { switch (type) {
LoggerMessage logmsg(shortFile,context.line,context.function,textType,context.category,msg, QLatin1String("")); case QtDebugMsg:
sConsumer->pushMessage(logmsg); // emiting message textType = QStringLiteral("DEBUG");
} colorTextType = Logger::toColor("\033[0;31m", textType);
break;
// Output message case QtWarningMsg:
//text += QString("%1 %2:%3 %4").arg(context.category).arg(shortFile).arg(context.line).arg(msg); textType = QStringLiteral("WARN");
// New output syntax colorTextType = Logger::toColor("\033[0;33m", textType);
text = QStringLiteral("[%1] %2 [ %3:%4 | %5 | %6 ]") break;
.arg(textType, msg, shortFile) case QtCriticalMsg:
.arg(context.line) textType = QStringLiteral("CRITICAL");
.arg(context.function, context.category); colorTextType = Logger::toColor("\033[0;31m", textType);
break;
QTextStream out(stderr); case QtFatalMsg:
out << text << "\n"; //.toLocal8Bit() + "\n"; textType = QStringLiteral("FATAL");
//out.flush(); // INVSDK-37 ORK-102 crash on windows when flush() -> possible threading problem colorTextType = Logger::toColor("\033[0;31m", textType);
break;
if (type == QtFatalMsg) #if QT_VERSION >= 0x050500
abort(); case QtInfoMsg:
} textType = QStringLiteral("INFO");
colorTextType = Logger::toColor("\033[0;34m", textType);
/** Passing data to consumer */ break;
void Logger::pushMessage(const LoggerMessage& logmsg) #endif
{ }
emit sConsumer->messageReceived(logmsg); if (sConsumer) {
QMutexLocker locker(&sConsumer->mMutex); LoggerMessage logmsg(shortFile,context.line,context.function,textType,context.category,msg, QLatin1String(""));
sConsumer->mLogMessages.append(logmsg); sConsumer->pushMessage(logmsg); // emiting message
emit sConsumer->messageListUpdated(); }
}
// Output message
} // core //text += QString("%1 %2:%3 %4").arg(context.category).arg(shortFile).arg(context.line).arg(msg);
// New output syntax
QDateTime now = QDateTime::currentDateTime();
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);
out << text << "\n"; //.toLocal8Bit() + "\n";
if (type == QtFatalMsg)
abort();
}
/** Passing data to consumer */
void Logger::pushMessage(const LoggerMessage& logmsg)
{
emit sConsumer->messageReceived(logmsg);
QMutexLocker locker(&sConsumer->mMutex);
sConsumer->mLogMessages.append(logmsg);
emit sConsumer->messageListUpdated();
}
} // core

View File

@ -1,93 +1,113 @@
#pragma once #pragma once
#include "loggermessage.h" #include "loggermessage.h"
// Qt // Qt
#include <QObject> #include <QObject>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMutex> #include <QMutex>
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)
namespace serversdk { Q_DECLARE_LOGGING_CATEGORY(sdkdb)
/** namespace serversdk {
* # Loggger
* /** # Loggger
* Class for logging qDebug qWarning qInfo and qCritical messages *
* * Class for logging qDebug qWarning qInfo and qCritical messages
* *
* ## Basic usage *
* * ## Basic usage
* @code *
* int main() { * @code
* serversdk::initAppLogger(); // <-- then Logger::outputMessage will handle all messages * int main() {
* // Log messge Filtering * serversdk::initAppLogger(); // <-- then Logger::outputMessage will handle all messages
* QStringList filters; * // Log messge Filtering
* filters << "qt.*=false"; * QStringList filters;
* filters << "sdk.core.*=true"; * filters << "qt.*=false";
* QLoggingCategory::setFilterRules(filters.join(QStringLiteral("\n"))); * filters << "sdk.core.*=true";
* } * QLoggingCategory::setFilterRules(filters.join(QStringLiteral("\n")));
* @endcode * }
* * @endcode
* @see https://doc.qt.io/qt-5/qdebug.html *
* @see https://doc.qt.io/qt-5/qloggingcategory.html * @see https://doc.qt.io/qt-5/qdebug.html
* * @see https://doc.qt.io/qt-5/qloggingcategory.html
*/ *
class Logger : public QObject */
{ class Logger : public QObject
Q_OBJECT {
public: Q_OBJECT
// User defined consumer ( messages will be sent there using insertMessage ) public:
static Logger* sConsumer; // User defined consumer ( messages will be sent there using insertMessage )
static Logger* sConsumer;
/// Default constructor
explicit Logger(QObject *parent = nullptr); /**
* # Default values
*
/** * * bits - Expected 32bits
* @brief User specific consumer of logger messages * * subjectId - must be valid ( not possible to add token withou valid subject )
* @param consumer - sublass of serversdk::Loger *
* * @m_class{m-block m-error}
* @code
* #include "core.h" @par Third-party license info
* int main() { This utility depends on the [Magnum engine](https://magnum.graphics).
* serversdk::initAppLogger(); It's licensed under MIT, so all you need to do is mention it in the
* serversdk::Logger logger; credits of your commercial app.
* serversdk::Logger::setConsumer(&logger); *
* } *
* @endcode */
* explicit Logger(QObject *parent = nullptr);
*/
static void setConsumer(Logger *consumer);
/**
* @brief User specific consumer of logger messages
/** Only Message handler for Qt * @param consumer - sublass of serversdk::Logger
* *
* @code * @code
* #include "core.h" * #include "core.h"
* int main() { * int main() {
* serversdk::initAppLogger(); * serversdk::initAppLogger();
* } * serversdk::Logger logger;
* @endcode * serversdk::Logger::setConsumer(&logger);
*/ * }
static void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg); * @endcode
*
/** Pusing messges from installed message handler */ */
virtual void pushMessage(const LoggerMessage& logmsg); static void setConsumer(Logger *consumer);
QList<serversdk::LoggerMessage> getNewMessages(int currentCount);
/** Only Message handler for Qt
signals: *
void messageReceived(const serversdk::LoggerMessage &message); * @code
void messageListUpdated(); * #include "core.h"
private: * int main() {
QList<LoggerMessage> mLogMessages; * serversdk::initAppLogger();
QMutex mMutex; * }
}; * @endcode
*/
static void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
} // core
/** Pushing messges from installed message handler */
virtual void pushMessage(const LoggerMessage& logmsg);
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:
void messageReceived(const serversdk::LoggerMessage &message);
void messageListUpdated();
private:
QList<LoggerMessage> mLogMessages; //!< Test
QMutex mMutex;
static bool mColorOutput;
};
} // core

View File

@ -1,113 +1,170 @@
#include "request.h" #include "request.h"
#include "logger.h" #include "logger.h"
#include "requestmanager.h" // to get logger class #include "requestmanager.h" // to get logger class
#include <QDebug> #include <QDebug>
#include <utility> #include <utility>
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()
QNetworkRequest Request::networkRequest() const {
{ if (mNetworkReply)
QNetworkRequest request(QUrl(this->mUrl)); mNetworkReply->deleteLater();
}
if ( !userName().isEmpty() && !password().isEmpty() )
request.setRawHeader("Authorization", this->basicAuthorization().toUtf8()); QNetworkRequest Request::networkRequest() const
{
if ( !token().isEmpty() ) { QNetworkRequest request(QUrl(this->mUrl));
request.setRawHeader("Authorization", this->bearerAuthorization().toUtf8()); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
}
if ( !userName().isEmpty() ) {
// Enabled compression request.setRawHeader("Authorization", this->basicAuthorization().toUtf8());
#ifdef CORESDK_ZLIB }
request.setRawHeader("Accept-Encoding", "gzip, deflate");
#endif if ( !token().isEmpty() ) {
request.setRawHeader("Authorization", this->bearerAuthorization().toUtf8());
// Enabling HTTP/2 ( not supported on Ubuntu 16.04 ) }
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
return request; // Enabled compression
} #ifdef CORESDK_ZLIB
request.setRawHeader("Accept-Encoding", "gzip, deflate");
#endif
void Request::appendData(const QByteArray &array)
{ // Enabling HTTP/2 ( does not work on Qt 5.9.8 )
mResponseData += array; // request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
} return request;
}
QString Request::url() const
{
return mUrl; int Request::status() const
} {
Q_ASSERT(mNetworkReply);
void Request::setUrl(const QString &url)
{ if (!mNetworkReply->isFinished())
mUrl = url; return 0;
}
auto variant = mNetworkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QByteArray Request::responseData() const if (!variant.isValid()) {
{ return -1;
return mResponseData; }
} return variant.toInt();
}
QByteArray Request::requestData() const
{
return mRequestData; void Request::appendResponseData(const QByteArray &array)
} {
mResponseData += array;
void Request::setRequestData(const QByteArray &requestData) }
{
mRequestData = requestData; void Request::setNetworkReply(QNetworkReply *networkReply)
} {
Q_ASSERT(networkReply);
QString Request::token() const mNetworkReply = networkReply;
{ }
return mToken;
} QNetworkReply *Request::networkReply() const
{
void Request::setToken(const QString &token) return mNetworkReply;
{ }
mToken = token;
} QString Request::type() const
{
QString Request::userName() const return mType;
{ }
return mUserName;
} void Request::setType(const QString &type)
{
void Request::setUserName(const QString &username) mType = type;
{ }
mUserName = username;
} QString Request::url() const
{
QString Request::password() const return mUrl;
{ }
return mPassword;
} void Request::setUrl(const QString &url)
{
void Request::setPassword(const QString &password) mUrl = url;
{ }
mPassword = password;
} QByteArray Request::responseData() const
{
QString Request::basicAuthorization() const return mResponseData;
{ }
// HTTP Basic authentication header value: base64(username:password)
QString concatenated = userName() + ":" + password(); QByteArray Request::requestData() const
QByteArray data = concatenated.toLocal8Bit().toBase64(); {
QString headerData = "Basic " + data; return mRequestData;
return headerData; }
//request.setRawHeader("Authorization", headerData.toLocal8Bit());
} void Request::setRequestData(const QByteArray &requestData)
{
QString Request::bearerAuthorization() const mRequestData = requestData;
{ }
qCDebug(sdknetwork) << "not implemented yet";
return QString(); QString Request::token() const
} {
return mToken;
} // core }
void Request::setToken(const QString &token)
{
mToken = token;
}
QString Request::userName() const
{
return mUserName;
}
void Request::setUserName(const QString &username)
{
mUserName = username;
}
QString Request::password() const
{
return mPassword;
}
void Request::setPassword(const QString &password)
{
mPassword = password;
}
QString Request::basicAuthorization() const
{
// HTTP Basic authentication header value: base64(username:password)
QString concatenated = userName() + ":" + password();
QByteArray data = concatenated.toLocal8Bit().toBase64();
QString headerData = "Basic " + data;
return headerData;
}
QString Request::bearerAuthorization() const
{
qCDebug(sdknetwork) << "not implemented yet";
return QString();
}
qint64 Request::getProcessTime()
{
if (!mStartTime.isNull() && !mFinishTime.isNull()) {
return mStartTime.msecsTo(mFinishTime);
}
return -1;
}
} // core
QDebug operator <<(QDebug dbg, serversdk::Request *request)
{
dbg.noquote() << request->type() << request->url() << "data:" << request->requestData() << "header:" << request->networkReply()->rawHeaderList();
return dbg.maybeSpace();
}

View File

@ -1,64 +1,85 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDateTime>
namespace serversdk { #include <QNetworkReply>
#include <memory>
class Request : public QObject {
Q_OBJECT namespace serversdk {
friend class RequestManager;
friend class RequestTest; class Request : public QObject {
public: Q_OBJECT
Request() = default; friend class RequestManager;
~Request() override; friend class RequestTest;
Request(QString type, QString url, QByteArray responseData = QByteArray()); public:
virtual QNetworkRequest networkRequest() const; Request() = default;
~Request() override;
// Data handling data Request(QString type, QString url, QByteArray requestData = QByteArray());
QByteArray requestData() const; virtual QNetworkRequest networkRequest() const;
void setRequestData(const QByteArray &requestData);
QByteArray responseData() const; // Data handling data
int status() const;
// Attributes QNetworkReply *networkReply() const;
QString url() const; void setNetworkReply(QNetworkReply *networkReply);
void setUrl(const QString &url); QByteArray requestData() const;
void setRequestData(const QByteArray &requestData);
QByteArray responseData() const;
// Authorization // Attributes
QString userName() const; QString url() const;
void setUserName(const QString &userName); void setUrl(const QString &url);
QString password() const; QString type() const;
void setPassword(const QString &password); void setType(const QString &type);
QString token() const;
void setToken(const QString &token); // Authorization
QString userName() const;
// Authorization headers void setUserName(const QString &userName);
QString basicAuthorization() const; QString password() const;
QString bearerAuthorization() const; void setPassword(const QString &password);
QString token() const;
void setToken(const QString &token);
private:
// Data handling // Authorization headers
void appendData(const QByteArray &array); QString basicAuthorization() const;
QString bearerAuthorization() const;
signals: qint64 getProcessTime();
void finished();
private: private:
QString mType; // POST, GET // Additional attributes
QString mUrl; void logStartTime() { mStartTime = QDateTime::currentDateTime(); }
void logFinishTime() { mFinishTime = QDateTime::currentDateTime(); }
// Authorization // Data handling
QString mUserName; void appendResponseData(const QByteArray &array);
QString mPassword;
QString mToken;
signals:
// Data void finished(std::shared_ptr<Request> request);
QByteArray mRequestData;
QByteArray mResponseData; private:
}; QString mType = "GET"; // POST, GET
QString mUrl;
} // core
// Authorization
QString mUserName;
QString mPassword;
QString mToken;
// Data
QNetworkReply *mNetworkReply = nullptr;
QByteArray mRequestData;
QByteArray mResponseData;
// Additional attributes
QDateTime mStartTime;
QDateTime mFinishTime;
};
} // core
QDebug operator<<(QDebug dbg, serversdk::Request *request);

View File

@ -1,95 +1,148 @@
#include "requestmanager.h" #include "requestmanager.h"
#include "request.h" #include "request.h"
#ifdef CORESDK_ZLIB #ifdef CORESDK_ZLIB
#include "compression.h" #include "compression.h"
#endif #endif
#include <memory> #include <memory>
namespace serversdk { namespace serversdk {
/** /**
* @brief Wrapper around QNetworkAccessManger to handle soap protocol * @brief Wrapper around QNetworkAccessManger to handle soap protocol
* @param parent * @param parent
*/ */
RequestManager::RequestManager(QObject *parent) RequestManager::RequestManager(QObject *parent)
: QObject(parent) : 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 Soap::downloadRequest * @brief Process request using QNetworkAccessManger
* @param soapRequest *
*/ * @param Request
void RequestManager::processRequest(const std::shared_ptr<Request>& request) */
{ bool RequestManager::processRequest(const std::shared_ptr<Request>& request)
QNetworkReply *reply = mNetworkAccessManager.post(request->networkRequest(), request->requestData()); {
mRequestMap.insert(reply, request); QNetworkReply *reply = nullptr;
request->logStartTime();
connect(reply, &QNetworkReply::sslErrors, this, &RequestManager::sslErrors);
connect(reply, &QNetworkReply::downloadProgress, this, &RequestManager::downloadProgress); if (request->type() == "POST") {
if ( request->requestData().isEmpty()) {
connect(reply, &QNetworkReply::finished, this, [=]() { qCWarning(sdknetwork) << "Request data is empty -> nothing to send";
std::shared_ptr<Request> request = mRequestMap.value(reply); }
mRequestMap.remove(reply); reply = mNetworkAccessManager.post(request->networkRequest(), request->requestData());
reply->deleteLater(); } else if (request->type() == "GET") {
emit request->finished(); if ( ! request->requestData().isEmpty()) {
emit downloadFinished(request); 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());
* @brief Handle sslErrors -> only debug output } else {
* @param sslErrors ( from QNetworkReply::sslErrors ) qCritical(sdknetwork) << "Using request type:" << request->type() << "is not implemented";
*/ return false;
void RequestManager::sslErrors(const QList<QSslError> &sslErrors) }
{
for (const QSslError &error : sslErrors) {
qCDebug(sdknetwork) << QStringLiteral("SSL error:") << error.errorString(); Q_ASSERT(reply);
} // Adding reply to final request
} reply->setParent(nullptr); // ensure QNetworkreply wont be deleted by QNetworkAccessManager ( it is parent of reply )
request->setNetworkReply(reply);
/**
* @brief called upon download progess -> adding data into QByteArray // Add to map to get correct request when reply is used in signal
* @param bytesReceived ( can be different from actual data -> compression ) mRequestMap.insert(reply, request);
* @param bytesTotal ( can be different from actual data -> compression )
*/ connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
void RequestManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) this, &RequestManager::replyError);
{ connect(reply, &QNetworkReply::downloadProgress, this, &RequestManager::downloadProgress);
// Get Reply ( now only one )
//Q_ASSERT(mCurrentReply); connect(reply, &QNetworkReply::finished, this, [=]() {
auto *reply = qobject_cast<QNetworkReply*>(sender()); std::shared_ptr<Request> request = mRequestMap.value(reply);
Q_ASSERT(mRequestMap.contains(reply)); request->logFinishTime();
std::shared_ptr<Request> request = mRequestMap.value(reply); mRequestMap.remove(reply);
//Q_ASSERT(reply == mCurrentReply); emit request->finished(request);
emit downloadFinished(request);
QString encoding; });
// Check encoding return true;
for (const auto & pair : reply->rawHeaderPairs()) { }
if (pair.first == QStringLiteral("Content-Encoding")) {
encoding = pair.second; /**
} * @brief Handle sslErrors -> only debug output
} * @param sslErrors ( from QNetworkReply::sslErrors )
*/
QByteArray data = reply->readAll(); void RequestManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &sslErrors)
{
// Decompress gzip data for (const QSslError &error : sslErrors) {
#ifdef CORESDK_ZLIB qCDebug(sdknetwork) << QStringLiteral("SSL error:") << error.errorString();
if (encoding.toLower() == "gzip" && !data.isEmpty() ) { }
data = Compression::decompress(data);
} if (ignoreSsl()) {
#endif reply->ignoreSslErrors(sslErrors);
}
// Append data to request }
request->appendData(data);
/**
emit progresss(bytesReceived, bytesTotal); * @brief called upon download progess -> adding data into QByteArray
} * @param bytesReceived ( can be different from actual data -> compression )
* @param bytesTotal ( can be different from actual data -> compression )
} // core */
void RequestManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
// Get Reply ( now only one )
//Q_ASSERT(mCurrentReply);
auto *reply = qobject_cast<QNetworkReply*>(sender());
Q_ASSERT(mRequestMap.contains(reply));
std::shared_ptr<Request> request = mRequestMap.value(reply);
//Q_ASSERT(reply == mCurrentReply);
QString encoding;
// Check encoding
for (const auto & pair : reply->rawHeaderPairs()) {
if (pair.first == QStringLiteral("Content-Encoding")) {
encoding = pair.second;
}
}
QByteArray data = reply->readAll();
// Decompress gzip data
#ifdef CORESDK_ZLIB
if (encoding.toLower() == "gzip" && !data.isEmpty() ) {
data = Compression::decompress(data);
}
#endif
// Append data to request
request->appendResponseData(data);
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

View File

@ -1,39 +1,43 @@
#pragma once #pragma once
#include "logger.h" #include "logger.h"
#include "request.h" #include "request.h"
#include <QtNetwork> #include <QtNetwork>
#include <memory> #include <memory>
namespace serversdk { namespace serversdk {
/** /**
* @brief Will download only ONE ITEM at a time * @brief Will download only ONE ITEM at a time
*/ */
class RequestManager : public QObject class RequestManager : public QObject
{ {
Q_OBJECT; Q_OBJECT;
friend class SoapTest; friend class SoapTest;
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);
private: bool ignoreSsl() const;
Q_SLOT void sslErrors(const QList<QSslError> &sslErrors); void setIgnoreSsl(bool ignoreSsl);
Q_SLOT void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
Q_SLOT void sslErrors(QNetworkReply *reply, const QList<QSslError> &sslErrors);
signals: Q_SLOT void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void downloadFinished(std::shared_ptr<Request> request); Q_SLOT void replyError(QNetworkReply::NetworkError code);
void progresss(qint64 bytesReceived, qint64 bytesTotal);
signals:
private: void downloadFinished(std::shared_ptr<Request> request);
QNetworkAccessManager mNetworkAccessManager; void progresss(qint64 bytesReceived, qint64 bytesTotal);
QMap<QNetworkReply*, std::shared_ptr<Request>> mRequestMap;
}; private:
QNetworkAccessManager mNetworkAccessManager;
} // core QMap<QNetworkReply*, std::shared_ptr<Request>> mRequestMap;
bool mIgnoreSsl = false;
};
} // core

View File

@ -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

View File

@ -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;
} }

View File

@ -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;

View File

@ -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
View 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

View File

@ -1,24 +1,15 @@
#include "serversdk/core.h" #include "serversdk/core.h"
#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) << "Application starting";
qCDebug(sdkcore) << "test message";
QCoreApplication app(argc, argv);
QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("example-app");
QCoreApplication::setApplicationName("colnod-connector"); QCoreApplication::setApplicationVersion("0.1.0");
QCoreApplication::setApplicationVersion("1.0"); }
// Command line parsing
QCommandLineParser parser;
parser.setApplicationDescription("Colnod sync");
QCommandLineOption dryModeOption("dryMode", "ColnodAPI", "Read-only mode, prints changes which would normally be made");
parser.addOption(dryModeOption);
parser.process(app);
}

View File

@ -1,7 +1,7 @@
TEMPLATE = app TEMPLATE = app
QT += core sql network QT += core sql network
SOURCES += main.cpp SOURCES += main.cpp
include(../../serversdkint.pri) include(../../serversdkint.pri)

13
test/auto/auto.pro Normal file
View 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

View 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"

View 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)

View 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"

View 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)

View 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"

View 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)

View 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"

View 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)

View 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"

View 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)

View File

@ -1,19 +1,28 @@
#include "tst_request.h" #include <QtTest>
#include "serversdk/request.h" #include "serversdk/request.h"
namespace serversdk { namespace serversdk {
void RequestTest::basicAuthorization_test() class RequestTest : public QObject
{ {
QString expected = "Basic VGVzdGVyOlRlc3Q="; Q_OBJECT
Request req; private:
req.setUserName("Tester"); Q_SLOT void basicAuthorization_test();
req.setPassword("Test"); };
QCOMPARE(req.basicAuthorization(), expected); void RequestTest::basicAuthorization_test()
} {
QString expected = "Basic VGVzdGVyOlRlc3Q=";
} Request req;
req.setUserName("Tester");
QTEST_APPLESS_MAIN(serversdk::RequestTest) req.setPassword("Test");
QCOMPARE(req.basicAuthorization(), expected);
}
}
QTEST_APPLESS_MAIN(serversdk::RequestTest)
#include "tst_request.moc"

View File

@ -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)

View 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"

View 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)

View 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)

View 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"

View 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)

View 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

Binary file not shown.

15
test/data/gzip.src Normal file
View 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>

View 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">&lt;?xml version=&apos;1.0&apos;?&gt;&lt;dwml version=&apos;1.0&apos; xmlns:xsd=&apos;http://www.w3.org/2001/XMLSchema&apos; xmlns:xsi=&apos;http://www.w3.org/2001/XMLSchema-instance&apos; xsi:noNamespaceSchemaLocation=&apos;https://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd&apos;&gt;&lt;latLonList&gt;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&lt;/latLonList&gt;&lt;/dwml&gt;</listLatLonOut></ns1:LatLonListSubgridResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

View 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>

View File

14
test/data/soap/bankapi-1.2.sh Executable file
View 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

View 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>

View 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
View 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

View 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
View 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();
}

View File

@ -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
View 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

View File

@ -1,5 +1,5 @@
TEMPLATE = subdirs TEMPLATE = subdirs
SUBDIRS += \ SUBDIRS += \
unittests auto \

View File

@ -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();
};
} //