mirror of
https://gitlab.com/spaceti-app/integrations/acs/colnod-connector.git
synced 2026-07-12 15:20:44 +02:00
Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9784c31174 | ||
|
|
c1a8c80cfa | ||
|
|
96b32c222c | ||
|
|
3c68cd64f2 | ||
|
|
91da3090c9 | ||
|
|
ce2a4cbaf7 | ||
|
|
111b93549a | ||
|
|
691ad920db | ||
|
|
d49db3f6b0 | ||
|
|
2bc1301fcd | ||
|
|
82d93fd115 | ||
|
|
d7b9c15dd6 | ||
|
|
18032ef1fc | ||
|
|
d91ff9f38f | ||
|
|
7dd5afe599 | ||
|
|
2dbd72bf34 | ||
|
|
7188e98e6d | ||
|
|
21da53fc33 | ||
|
|
30735dc953 | ||
|
|
9d7e05635e | ||
|
|
08afae47aa | ||
|
|
b405d95320 | ||
|
|
fe0954edf1 | ||
|
|
a19126588b | ||
|
|
fcb792447f | ||
|
|
319fc8d71e | ||
|
|
0f948afa71 | ||
|
|
10ac748985 | ||
|
|
3487c6f81c | ||
|
|
8cb0f3b01d | ||
|
|
d1a9c56f62 | ||
|
|
cca625ddec | ||
|
|
7e4a7bf074 | ||
|
|
443a76e50f | ||
|
|
79cebf9130 | ||
|
|
fd4440f940 | ||
|
|
26fbb6b281 | ||
|
|
e8e8184add | ||
|
|
7ac0df57e5 | ||
|
|
5dea3592e5 | ||
|
|
f3077d9f0a | ||
|
|
649b494682 | ||
|
|
9afde747c8 | ||
|
|
aeb2782920 | ||
|
|
63d480842e | ||
|
|
9d115ef5aa | ||
|
|
ac8eff481d | ||
|
|
dd0f8fe267 | ||
|
|
53697249c6 | ||
|
|
108eefcf16 | ||
|
|
1945e7e002 | ||
|
|
26a5d57f95 | ||
|
|
6ff3d7fcea | ||
|
|
53015d839a | ||
|
|
a88b200d5a | ||
|
|
dbd065c6d7 | ||
|
|
0dea65a0b6 | ||
|
|
e886a6698e | ||
|
|
098bc7fda8 | ||
|
|
3fbeb3130c | ||
|
|
7225ebe2ce | ||
|
|
08ca788358 | ||
|
|
1a0fd35c3d | ||
|
|
674b49163b | ||
|
|
f9a0d26283 | ||
|
|
9d2a411a01 | ||
|
|
f2097cb565 | ||
|
|
3bb6f24ae1 | ||
|
|
818416504b | ||
|
|
3709df6b6d | ||
|
|
7578c3e658 | ||
|
|
44b5a1baa6 | ||
|
|
ef0eefa741 | ||
|
|
1ca75b47d0 | ||
|
|
5cc57ac9e7 | ||
|
|
86f1f7aafe | ||
|
|
f5ea0ed1a0 | ||
|
|
cdf6241da4 | ||
|
|
c5e8a99a19 | ||
|
|
6d148994ee | ||
|
|
367376f283 | ||
|
|
ac6a327259 | ||
|
|
4af747fdd7 | ||
|
|
26fbc9b7a5 | ||
|
|
d334007f6c | ||
|
|
020adda943 | ||
|
|
33d7a7e37b | ||
|
|
b8785b4bcd | ||
|
|
c5cff0f216 | ||
|
|
6c9f061153 | ||
|
|
a8bcfc7f18 | ||
|
|
f1f14bfd3e | ||
|
|
9f1d0dbc55 | ||
|
|
a9852b88c1 | ||
|
|
1784b33f7f | ||
|
|
31833adf5c | ||
|
|
fc54831952 | ||
|
|
a3c9a783e7 | ||
|
|
9f9f287721 | ||
|
|
e52c293a41 | ||
|
|
44310bfd9d | ||
|
|
4fe612d11d | ||
|
|
e5fa284f03 | ||
|
|
97a00c098d | ||
|
|
d2e1883760 | ||
|
|
80c550ff03 | ||
|
|
2ce5a025a9 | ||
|
|
0eafa20283 | ||
|
|
25aa4e40ac | ||
|
|
3684e089f9 | ||
|
|
5bff3ce9f3 | ||
|
|
671baf83aa | ||
|
|
9d398777db | ||
|
|
900fda34e9 | ||
|
|
fc52975b61 | ||
|
|
ee07e06032 | ||
|
|
dea9da471c | ||
|
|
79c1fc36fe | ||
|
|
6d73a9c712 | ||
|
|
c96832f4b8 | ||
|
|
9bf2eb05e8 | ||
|
|
fcbb030ce9 | ||
|
|
ccb74554e4 | ||
|
|
eb4ed9cc4c | ||
|
|
0e9b20b28d | ||
|
|
0e377a3554 |
@ -24,6 +24,7 @@ build:
|
|||||||
- git submodule sync --recursive
|
- git submodule sync --recursive
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
script:
|
script:
|
||||||
|
- if [ -d build ]; then rm -rf build; fi
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- qmake $PRO_FILE
|
- qmake $PRO_FILE
|
||||||
@ -37,6 +38,7 @@ test:
|
|||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
script:
|
script:
|
||||||
|
- if [ -d build ]; then rm -rf build; fi
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- qmake $PRO_FILE
|
- qmake $PRO_FILE
|
||||||
@ -48,6 +50,7 @@ valgrind:
|
|||||||
tags:
|
tags:
|
||||||
- shell
|
- shell
|
||||||
script:
|
script:
|
||||||
|
- if [ -d build ]; then rm -rf build; fi
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- qmake $PRO_FILE
|
- qmake $PRO_FILE
|
||||||
|
|||||||
120
README.md
120
README.md
@ -169,5 +169,125 @@ It is possible to chain requests: [see doc](https://support.insomnia.rest/articl
|
|||||||
|
|
||||||
`npm install -g snowboard`
|
`npm install -g snowboard`
|
||||||
|
|
||||||
|
## Tests made
|
||||||
|
Changes on colnod are made by [Colnod client](https://spaceti.atlassian.net/wiki/spaces/IN/pages/194576388/Colnod+client)
|
||||||
|
|
||||||
|
Changes on db can be made by sql commands directly or by [pgAdmin](https://www.pgadmin.org/)
|
||||||
|
- add token to db/colnod (2 tests)
|
||||||
|
- edit token in db/colnod (2 tests)
|
||||||
|
- delete token from db/colnod (2 tests)
|
||||||
|
- add subject to colnod
|
||||||
|
- edit subject in colnod
|
||||||
|
- delete subject from colnod
|
||||||
|
- edit same token on both db/colnod (diffrent time)
|
||||||
|
|
||||||
|
## First sync expected results
|
||||||
|
- 2892 tokens updated
|
||||||
|
- 173 tokens deleted
|
||||||
|
- 2894 subjects updated
|
||||||
|
- 117 subjects deleted
|
||||||
|
|
||||||
|
## Database inicialization
|
||||||
|
- Token table
|
||||||
|
```SQL create
|
||||||
|
-- Table: public.colnod_token
|
||||||
|
-- DROP TABLE public.colnod_token;
|
||||||
|
CREATE TABLE public.colnod_token
|
||||||
|
(
|
||||||
|
id character(36) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
auth_errors integer DEFAULT 255,
|
||||||
|
description character varying(255) COLLATE pg_catalog."default",
|
||||||
|
enabled boolean DEFAULT true,
|
||||||
|
last_update timestamp without time zone DEFAULT '1970-01-01 00:00:00.001'::timestamp without time zone,
|
||||||
|
name character varying(255) COLLATE pg_catalog."default",
|
||||||
|
no_validity boolean DEFAULT true,
|
||||||
|
time_to_live integer DEFAULT 255,
|
||||||
|
token_bits integer DEFAULT 32,
|
||||||
|
token_data character varying(255) COLLATE pg_catalog."default",
|
||||||
|
token_type character varying(255) COLLATE pg_catalog."default" DEFAULT 'Token'::character varying,
|
||||||
|
subject_id character(36) COLLATE pg_catalog."default",
|
||||||
|
last_modified timestamp without time zone NOT NULL,
|
||||||
|
deleted boolean DEFAULT false,
|
||||||
|
CONSTRAINT colnod_token_pkey PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
)
|
||||||
|
TABLESPACE pg_default;
|
||||||
|
|
||||||
|
ALTER TABLE public.colnod_token
|
||||||
|
OWNER to postgres;
|
||||||
|
```
|
||||||
|
- Subject table
|
||||||
|
```SQL create
|
||||||
|
-- Table: public.colnod_subject
|
||||||
|
-- DROP TABLE public.colnod_subject;
|
||||||
|
CREATE TABLE public.colnod_subject
|
||||||
|
(
|
||||||
|
id character(36) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
description character varying(255) COLLATE pg_catalog."default",
|
||||||
|
last_update timestamp without time zone NOT NULL,
|
||||||
|
name character varying(255) COLLATE pg_catalog."default",
|
||||||
|
pin character varying(255) COLLATE pg_catalog."default",
|
||||||
|
last_modified timestamp without time zone,
|
||||||
|
deleted boolean,
|
||||||
|
CONSTRAINT colnod_subject_pkey PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
)
|
||||||
|
TABLESPACE pg_default;
|
||||||
|
|
||||||
|
ALTER TABLE public.colnod_subject
|
||||||
|
OWNER to postgres;
|
||||||
|
```
|
||||||
|
- Subject attributes table
|
||||||
|
```SQL create
|
||||||
|
-- Table: public.colnod_subject_attribute
|
||||||
|
-- DROP TABLE public.colnod_subject_attribute;
|
||||||
|
CREATE TABLE public.colnod_subject_attribute
|
||||||
|
(
|
||||||
|
id character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
attribute_key character varying(255) COLLATE pg_catalog."default",
|
||||||
|
attribute_value character varying(255) COLLATE pg_catalog."default",
|
||||||
|
subject_id character(36) COLLATE pg_catalog."default",
|
||||||
|
CONSTRAINT colnod_subject_attribute_pkey PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
)
|
||||||
|
TABLESPACE pg_default;
|
||||||
|
|
||||||
|
ALTER TABLE public.colnod_subject_attribute
|
||||||
|
OWNER to postgres;
|
||||||
|
```
|
||||||
|
- Config
|
||||||
|
```SQL create
|
||||||
|
-- Table: public.config
|
||||||
|
-- DROP TABLE public.config;
|
||||||
|
CREATE TABLE public.config
|
||||||
|
(
|
||||||
|
last_sync bigint NOT NULL DEFAULT 0,
|
||||||
|
CONSTRAINT config_pkey PRIMARY KEY (last_sync)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
)
|
||||||
|
TABLESPACE pg_default;
|
||||||
|
|
||||||
|
ALTER TABLE public.config
|
||||||
|
OWNER to postgres;
|
||||||
|
```
|
||||||
## Possible checks
|
## Possible checks
|
||||||
[here](https://gitlab.com/spaceti-app/integrations/acs/colnod-connector/blob/master/doc/possible-checks.md)
|
[here](https://gitlab.com/spaceti-app/integrations/acs/colnod-connector/blob/master/doc/possible-checks.md)
|
||||||
|
|
||||||
|
## Bugs and improvments for Colsys
|
||||||
|
### Bugs
|
||||||
|
- It is possible to add token with token ID (tokenData) which already exists
|
||||||
|
- No error message, when request data are invalid in login
|
||||||
|
- getUpdated returns valid data for invalid time (negative)
|
||||||
|
### Improvements
|
||||||
|
- it would be nice to get response messages on calling enpoints
|
||||||
|
- setToken -> successful edit/insert
|
||||||
|
- deleteToken -> successful
|
||||||
|
-
|
||||||
|
|||||||
@ -3,3 +3,5 @@
|
|||||||
|
|
||||||
## Cases:
|
## Cases:
|
||||||
- try to add new virtual token to subject who has no physical card
|
- try to add new virtual token to subject who has no physical card
|
||||||
|
- insert token with tokenData already exists
|
||||||
|
- token which has not "mobile" in description can't be pushed/deleted from colnod
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit d54da50d5ed3e0ce70b46bb13f8548a048c64120
|
Subproject commit d4c4d75b49fe7e0399a6c4e57a7bb32f82bee59d
|
||||||
@ -1,46 +1,58 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
#include "datamanager.h"
|
|
||||||
#include "syncmanager.h"
|
#include "syncmanager.h"
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
#include "serversdk/requestmanager.h"
|
||||||
|
#include "serversdk/request.h"
|
||||||
|
|
||||||
|
#include "serversdk/logger.h"
|
||||||
|
#include "serversdk/core.h"
|
||||||
|
|
||||||
|
#include "colnodapi.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
// App setup
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
QCoreApplication::setApplicationName("colnod-connector");
|
QCoreApplication::setApplicationName("colnod-connector");
|
||||||
QCoreApplication::setApplicationVersion("1.0");
|
QCoreApplication::setApplicationVersion("1.0");
|
||||||
|
|
||||||
|
// Command line parsing
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
Logger *logger = Logger::getLogger();
|
|
||||||
parser.setApplicationDescription("Colnod sync");
|
parser.setApplicationDescription("Colnod sync");
|
||||||
QCommandLineOption dryModeOption("dryMode", "ColnodAPI", "Read-only mode, prints changes which would normally be made");
|
QCommandLineOption dryModeOption("dryMode", "ColnodAPI", "Read-only mode, prints changes which would normally be made");
|
||||||
QCommandLineOption dropTablesOption("dropTables", "ColnodAPI", "Delete tables with datas from Colnod");
|
QCommandLineOption dropTablesOption("dropTables", "ColnodAPI", "Delete tables with datas from Colnod");
|
||||||
QCommandLineOption createTablesOption("createTables", "ColnodAPI", "Create tables to saving data from Colnod");
|
QCommandLineOption createTablesOption("createTables", "ColnodAPI", "Create tables for saving data from Colnod");
|
||||||
parser.addOption(dryModeOption);
|
parser.addOption(dryModeOption);
|
||||||
parser.addOption(dropTablesOption);
|
parser.addOption(dropTablesOption);
|
||||||
parser.addOption(createTablesOption);
|
parser.addOption(createTablesOption);
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
SyncManager api(parser.isSet(dryModeOption));
|
|
||||||
QObject::connect(&api, &SyncManager::error, &app, [](){
|
|
||||||
qDebug() << "error";
|
// Logger initializiaton
|
||||||
QCoreApplication::exit(1);
|
serversdk::initAppLogger();
|
||||||
}, Qt::QueuedConnection);
|
|
||||||
if (parser.isSet(dropTablesOption)){
|
// Log messge Filtering
|
||||||
api.dropTables();
|
QStringList filters;
|
||||||
return 0;
|
filters << "qt.*=false";
|
||||||
|
filters << "sdk*=false";
|
||||||
|
//filters << "*=false";
|
||||||
|
QLoggingCategory::setFilterRules(filters.join(QStringLiteral("\n")));
|
||||||
|
|
||||||
|
|
||||||
|
SyncManager syncManager(parser.isSet(dryModeOption));
|
||||||
|
|
||||||
|
// Initialize database and colnod API
|
||||||
|
if (!syncManager.databaseInitialization()) {
|
||||||
|
qCritical() << "Not possible to initialize sync application";
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
if (parser.isSet(createTablesOption)){
|
|
||||||
api.createTables();
|
// Synchronizaiton
|
||||||
return 0;
|
if (!syncManager.startSynchronization()) {
|
||||||
|
qCritical() << "Not possible to start synchronization";
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
api.loadDataFromDB();
|
|
||||||
api.loadDataFromColnod();
|
|
||||||
QObject::connect(&api, &SyncManager::dataDownloaded, [&](){
|
|
||||||
QObject::connect(&api, &SyncManager::syncFinished, [](){
|
|
||||||
qDebug() << "Quit";
|
|
||||||
QCoreApplication::exit();
|
|
||||||
});
|
|
||||||
api.syncDataFromColnodToDB();
|
|
||||||
api.syncDataFromDBToColnod();
|
|
||||||
});
|
|
||||||
return QCoreApplication::exec();
|
return QCoreApplication::exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,23 +4,17 @@ CONFIG += c++14
|
|||||||
|
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/databasemanager.cpp \
|
$$PWD/colnodapi.cpp \
|
||||||
$$PWD/datamanager.cpp \
|
$$PWD/data.cpp \
|
||||||
$$PWD/entity.cpp \
|
$$PWD/databaseapi.cpp \
|
||||||
$$PWD/logger.cpp \
|
|
||||||
$$PWD/requestmanager.cpp \
|
|
||||||
$$PWD/response.cpp \
|
|
||||||
$$PWD/subject.cpp \
|
$$PWD/subject.cpp \
|
||||||
$$PWD/syncmanager.cpp \
|
$$PWD/syncmanager.cpp \
|
||||||
$$PWD/token.cpp
|
$$PWD/token.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/databasemanager.h \
|
$$PWD/colnodapi.h \
|
||||||
$$PWD/datamanager.h \
|
$$PWD/data.h \
|
||||||
$$PWD/entity.h \
|
$$PWD/databaseapi.h \
|
||||||
$$PWD/logger.h \
|
|
||||||
$$PWD/requestmanager.h \
|
|
||||||
$$PWD/response.h \
|
|
||||||
$$PWD/subject.h \
|
$$PWD/subject.h \
|
||||||
$$PWD/syncmanager.h \
|
$$PWD/syncmanager.h \
|
||||||
$$PWD/token.h
|
$$PWD/token.h
|
||||||
|
|||||||
384
src/colnod/colnodapi.cpp
Normal file
384
src/colnod/colnodapi.cpp
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
#include "colnodapi.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(colnod, "colnod.api");
|
||||||
|
|
||||||
|
|
||||||
|
ColnodAPI::ColnodAPI(serversdk::RequestManager* reqMan, const bool &dryMode, QString userName, QString password)
|
||||||
|
:mReqMan(reqMan), mUserName(std::move(userName)), mPassword(std::move(password)), mDryMode(dryMode)
|
||||||
|
{
|
||||||
|
// -----------------------------------
|
||||||
|
mReqMan->setIgnoreSsl(true);
|
||||||
|
// -----------------------------------
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Downloads new data from Colnod -> emits downloadDataFinished signal
|
||||||
|
* @param time
|
||||||
|
*
|
||||||
|
* @return signal with nullptr when something went wrong
|
||||||
|
* @return signal with data when valid data returned
|
||||||
|
*/
|
||||||
|
bool ColnodAPI::downloadData(const qint64 &time)
|
||||||
|
{
|
||||||
|
// Have to get Login
|
||||||
|
auto login = requestLogin();
|
||||||
|
Q_ASSERT(login);
|
||||||
|
if (login->networkReply() == nullptr) {
|
||||||
|
qCCritical(colnod) << "Not possible to create login request";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
connect(login.get(), &serversdk::Request::finished, this, [=](){
|
||||||
|
// Get login token and check if is correct
|
||||||
|
if (!checkRequest(login)) {
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AuthToken token;
|
||||||
|
|
||||||
|
if (checkResponse(login->responseData())) {
|
||||||
|
qCWarning(colnod) << "empty response data when logging in";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
token.fromJson(login->responseData());
|
||||||
|
authToken = token.id();
|
||||||
|
if (authToken.isEmpty()) {
|
||||||
|
qCCritical(colnod) << "Token uuid is empty";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = std::make_shared<Data>();
|
||||||
|
Timestamp timestamp;
|
||||||
|
timestamp.setTimestamp(time);
|
||||||
|
|
||||||
|
// emit signal when with all data when it is ready
|
||||||
|
auto checkData = [=]() {
|
||||||
|
int dataCount = data->incrementCounter();
|
||||||
|
if ( dataCount > 5) {
|
||||||
|
qCCritical(colnod) << "check data cannnot be higher then 4";
|
||||||
|
}
|
||||||
|
Q_ASSERT(dataCount < 5);
|
||||||
|
if (dataCount == 4) {
|
||||||
|
qInfo(colnod) << tab << "- src col:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() <<
|
||||||
|
"] subjects [" << data->updatedSubjects.size() << ":" << data->deletedSubjects.size() << "] [ added/updated : deleted ]";
|
||||||
|
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
||||||
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
||||||
|
qInfo(colnod) << dash;
|
||||||
|
}
|
||||||
|
for (const auto &token : data->updatedTokens) {
|
||||||
|
qInfo(colnod) << Message::massageTemplate("Download updated", "token", "colnod", token->name());
|
||||||
|
}
|
||||||
|
for (const auto &id : data->deletedTokens) {
|
||||||
|
qInfo(colnod) << Message::massageTemplate("Download deleted", "token", "colnod", id);
|
||||||
|
}
|
||||||
|
for (const auto &subject : data->updatedSubjects) {
|
||||||
|
qInfo(colnod) << Message::massageTemplate("Download updated", "subject", "colnod", subject->name());
|
||||||
|
}
|
||||||
|
for (const auto &id : data->deletedSubjects) {
|
||||||
|
qInfo(colnod) << Message::massageTemplate("Download deleted", "token", "colnod", id);
|
||||||
|
}
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
||||||
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
||||||
|
qInfo(colnod) << dash;
|
||||||
|
}
|
||||||
|
emit downloadDataFinished(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto tokensRequest = getTokensUpdated(timestamp);
|
||||||
|
connect(tokensRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
if (!checkErrors(tokensRequest)) {
|
||||||
|
qCritical(colnod) << "Error during call getTokensUpdated endpoint";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!saveUpdatedTokens(data, tokensRequest->responseData())) {
|
||||||
|
qCritical(colnod) << "Error during saving getTokensUpdated";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkData();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto deletedTokensRequest = getTokensDeleted(timestamp);
|
||||||
|
connect(deletedTokensRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
if (!checkErrors(deletedTokensRequest)) {
|
||||||
|
qCritical(colnod) << "Error during call getTokensDeleted endpoint";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!saveDeletedTokens(data, deletedTokensRequest->responseData())) {
|
||||||
|
qCritical(colnod) << "Error during saving getTokensDeleted";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkData();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto subjectsRequest = getSubjectsUpdated(timestamp);
|
||||||
|
connect(subjectsRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
if (!checkErrors(subjectsRequest)) {
|
||||||
|
qCritical(colnod) << "Error during call getSubjectsUpdated endpoint";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!saveUpdatedSubjects(data, subjectsRequest->responseData())) {
|
||||||
|
qCritical(colnod) << "Error during saving getSubjectsUpdated";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkData();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto deletedSubjectsRequest = getSubjectsDeleted(timestamp);
|
||||||
|
connect(deletedSubjectsRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
if (!checkErrors(deletedSubjectsRequest)) {
|
||||||
|
qCritical(colnod) << "Error during call getSubjectsDeleted endpoint";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!saveDeletedSubjects(data, deletedSubjectsRequest->responseData())) {
|
||||||
|
qCritical(colnod) << "Error during saving getSubjectsDeleted";
|
||||||
|
emit downloadDataFinished(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkData();
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColnodAPI::processData(const std::shared_ptr<Data> &data)
|
||||||
|
{
|
||||||
|
// emit signal when finished
|
||||||
|
auto checkData = [=]() {
|
||||||
|
int dataCount = data->incrementCounter();
|
||||||
|
if ( dataCount > 3) {
|
||||||
|
qCCritical(colnod) << "check data cannnot be higher then 2";
|
||||||
|
}
|
||||||
|
Q_ASSERT(dataCount < 3);
|
||||||
|
if (dataCount == 2) {
|
||||||
|
qInfo(colnod) << tab << "- push col:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() <<
|
||||||
|
"] [ added/updated : deleted ]";
|
||||||
|
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
||||||
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
||||||
|
qInfo(colnod) << dash;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &token : data->updatedTokens) {
|
||||||
|
qInfo(colnod).noquote() << Message::massageTemplate("Updated", "token", "colnod", token->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &id : data->deletedTokens) {
|
||||||
|
qInfo(colnod).noquote() << Message::massageTemplate("Deleted", "token", "colnod", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
||||||
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
||||||
|
qInfo(colnod) << dash;
|
||||||
|
}
|
||||||
|
emit dataProcessFinished(mValid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( !data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ) {
|
||||||
|
qInfo(colnod) << dash;
|
||||||
|
}
|
||||||
|
if ( !data->updatedTokens.isEmpty() && !mDryMode ) {
|
||||||
|
auto setTokensRequest = setTokens(data->updatedTokens);
|
||||||
|
connect(setTokensRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
if (!checkErrors(setTokensRequest)) {
|
||||||
|
data->updatedTokens.clear();
|
||||||
|
mValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkData();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
checkData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !data->deletedTokens.isEmpty() && !mDryMode ) {
|
||||||
|
auto deletedTokensRequest = deleteTokens(data->deletedTokens);
|
||||||
|
connect(deletedTokensRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
if (!checkErrors(deletedTokensRequest)) {
|
||||||
|
data->deletedTokens.clear();
|
||||||
|
mValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkData();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
checkData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::requestLogin()
|
||||||
|
{
|
||||||
|
Q_ASSERT(mReqMan);
|
||||||
|
LoginResp login;
|
||||||
|
login.setUsername(userName());
|
||||||
|
|
||||||
|
// password
|
||||||
|
QString encodedPassword = QCryptographicHash::hash(
|
||||||
|
password().toLocal8Bit(), // -> bytearray
|
||||||
|
QCryptographicHash::Sha512 // SHA 512
|
||||||
|
).toHex().toUpper();
|
||||||
|
login.setPassword(encodedPassword);
|
||||||
|
|
||||||
|
auto loginRequest = composeRequest("/AaaManager/authSession", login.jsonData());
|
||||||
|
loginRequest->setUserName(login.username());
|
||||||
|
loginRequest->setPassword(login.password());
|
||||||
|
mReqMan->processRequest(loginRequest);
|
||||||
|
|
||||||
|
return loginRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::getTokensUpdated(const Timestamp &time)
|
||||||
|
{
|
||||||
|
auto request = composeRequest("/TokenManager/getTokensUpdated", time.jsonData());
|
||||||
|
request->setUserName(authToken);
|
||||||
|
mReqMan->processRequest(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::getTokensDeleted(const Timestamp &time)
|
||||||
|
{
|
||||||
|
auto request = composeRequest("/TokenManager/getTokensDeleted", time.jsonData());
|
||||||
|
request->setUserName(authToken);
|
||||||
|
mReqMan->processRequest(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::getSubjectsUpdated(const Timestamp &time)
|
||||||
|
{
|
||||||
|
auto request = composeRequest("/SubjectManager/getSubjectsUpdated", time.jsonData());
|
||||||
|
request->setUserName(authToken);
|
||||||
|
mReqMan->processRequest(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::getSubjectsDeleted(const Timestamp &time)
|
||||||
|
{
|
||||||
|
auto request = composeRequest("/SubjectManager/getSubjectsDeleted", time.jsonData());
|
||||||
|
request->setUserName(authToken);
|
||||||
|
mReqMan->processRequest(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::setTokens(const QHash<QString, std::shared_ptr<Token>> &tokens)
|
||||||
|
{
|
||||||
|
auto request = composeRequest("/TokenManager/setTokens", Token::tokensToColnodJson(tokens));
|
||||||
|
request->setUserName(authToken);
|
||||||
|
mReqMan->processRequest(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::deleteTokens(const QList<QString> &ids)
|
||||||
|
{
|
||||||
|
auto request = composeRequest("/TokenManager/deleteTokens", Token::idsToJSON(ids));
|
||||||
|
request->setUserName(authToken);
|
||||||
|
mReqMan->processRequest(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> ColnodAPI::composeRequest(const QString &endpoint, const QByteArray &data)
|
||||||
|
{
|
||||||
|
QString url = mHost + endpoint;
|
||||||
|
return std::make_shared<serversdk::Request>("POST", url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::checkRequest(const std::shared_ptr<serversdk::Request> &request)
|
||||||
|
{
|
||||||
|
return request->status() == 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::checkErrors(const std::shared_ptr<serversdk::Request> &request)
|
||||||
|
{
|
||||||
|
if (!checkRequest(request)) {
|
||||||
|
qCWarning(colnod) << "Status is not 200";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checkResponse(request->responseData())) {
|
||||||
|
qCWarning(colnod) << "Empty response data";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::isValid()
|
||||||
|
{
|
||||||
|
return mValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ColnodAPI::password() const
|
||||||
|
{
|
||||||
|
return mPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColnodAPI::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
mPassword = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ColnodAPI::userName() const
|
||||||
|
{
|
||||||
|
return mUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColnodAPI::setUserName(const QString &userName)
|
||||||
|
{
|
||||||
|
mUserName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::saveUpdatedTokens(const std::shared_ptr<Data>& data, const QByteArray &responseData)
|
||||||
|
{
|
||||||
|
Q_ASSERT(data);
|
||||||
|
return data->loadTokensFromJson(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::saveDeletedTokens(const std::shared_ptr<Data>& data, const QByteArray &responseData)
|
||||||
|
{
|
||||||
|
Q_ASSERT(data);
|
||||||
|
if (responseData.isEmpty())
|
||||||
|
return false;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(responseData);
|
||||||
|
QJsonArray items = doc.object()["items"].toArray();
|
||||||
|
for (QJsonValueRef item : items) {
|
||||||
|
data->deletedTokens.append(item.toString());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::saveUpdatedSubjects(const std::shared_ptr<Data>& data, const QByteArray &responseData)
|
||||||
|
{
|
||||||
|
Q_ASSERT(data);
|
||||||
|
return data->loadSubjectsFormJson(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::saveDeletedSubjects(const std::shared_ptr<Data>& data, const QByteArray &responseData)
|
||||||
|
{
|
||||||
|
Q_ASSERT(data);
|
||||||
|
if (responseData.isEmpty())
|
||||||
|
return false;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(responseData);
|
||||||
|
QJsonArray items = doc.object()["items"].toArray();
|
||||||
|
for (QJsonValueRef item : items) {
|
||||||
|
data->deletedSubjects.append(item.toString());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColnodAPI::checkResponse(const QByteArray &responseData)
|
||||||
|
{
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(responseData);
|
||||||
|
return doc.object().contains("items");
|
||||||
|
}
|
||||||
137
src/colnod/colnodapi.h
Normal file
137
src/colnod/colnodapi.h
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "serversdk/requestmanager.h"
|
||||||
|
#include "serversdk/request.h"
|
||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
#include "serversdk/logger.h"
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(colnod)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper class for simply work with Colnod API
|
||||||
|
*/
|
||||||
|
class LoginResp : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, username, setUsername, "")
|
||||||
|
TABLE_PROPERTY(QString, password, setPassword, "")
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @brief Helper class for simply work with Colnod API
|
||||||
|
*/
|
||||||
|
class AuthToken : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, id, setId, QString())
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Class for easy switching date time formats
|
||||||
|
* Especially between from and to miliseconds
|
||||||
|
*/
|
||||||
|
class Timestamp : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(qint64, timestamp, setTimestamp, 0)
|
||||||
|
public:
|
||||||
|
Timestamp() = default;
|
||||||
|
|
||||||
|
void setTimestamp(const QDateTime &time)
|
||||||
|
{
|
||||||
|
setTimestamp(time.toMSecsSinceEpoch());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @breif Function for output date in readable form
|
||||||
|
*
|
||||||
|
* @return String in format: 2019-11-12 16:34:03.932
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QString toDateString()
|
||||||
|
{
|
||||||
|
return QDateTime::fromMSecsSinceEpoch(a_timestamp).toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ColnodAPI: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class ColnodTest;
|
||||||
|
public:
|
||||||
|
ColnodAPI() = default;
|
||||||
|
ColnodAPI(serversdk::RequestManager* reqManconst, const bool &dryMode = false, QString userName = "spaceti", QString password = "spaceti1");
|
||||||
|
/**
|
||||||
|
* @brief Downloading changed data from Colnod since last Synchronization
|
||||||
|
*
|
||||||
|
* @param time Time of last sync
|
||||||
|
*
|
||||||
|
* @return Function returns data changed after this time
|
||||||
|
*/
|
||||||
|
bool downloadData(const qint64 &time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push all data prepare during synchronization to Colnod
|
||||||
|
* @param data Data which are pushing to Colnod
|
||||||
|
*/
|
||||||
|
void processData(const std::shared_ptr<Data> &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Endpoint of Colnod API
|
||||||
|
* @param time Returns changed tokens after time
|
||||||
|
* @return Return request, than waiting for finished
|
||||||
|
*/
|
||||||
|
std::shared_ptr<serversdk::Request> getTokensUpdated(const Timestamp &time);
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
QString userName() const;
|
||||||
|
void setUserName(const QString &userName);
|
||||||
|
QString password() const;
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
|
||||||
|
bool isValid();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<serversdk::Request> requestLogin();
|
||||||
|
|
||||||
|
// All endpoints of Colnod API that we need to use, simply returns request
|
||||||
|
// than we wait for finish
|
||||||
|
std::shared_ptr<serversdk::Request> getTokensDeleted(const Timestamp &time);
|
||||||
|
std::shared_ptr<serversdk::Request> getSubjectsUpdated(const Timestamp &time);
|
||||||
|
std::shared_ptr<serversdk::Request> getSubjectsDeleted(const Timestamp &time);
|
||||||
|
|
||||||
|
std::shared_ptr<serversdk::Request> setTokens(const QHash<QString, std::shared_ptr<Token>> &tokens);
|
||||||
|
std::shared_ptr<serversdk::Request> deleteTokens(const QList<QString> &tokenIds);
|
||||||
|
|
||||||
|
// Functions which saves response data from Colnod to variable data
|
||||||
|
bool saveUpdatedTokens(const std::shared_ptr<Data>& data, const QByteArray &responseData);
|
||||||
|
bool saveUpdatedSubjects(const std::shared_ptr<Data>& data, const QByteArray &responseData);
|
||||||
|
bool saveDeletedTokens(const std::shared_ptr<Data>& data, const QByteArray &responseData);
|
||||||
|
bool saveDeletedSubjects(const std::shared_ptr<Data>& data, const QByteArray &responseData);
|
||||||
|
|
||||||
|
// Helper func for check format of response
|
||||||
|
bool checkResponse(const QByteArray &responseData);
|
||||||
|
// Helper func for check status of request
|
||||||
|
bool checkRequest(const std::shared_ptr<serversdk::Request> &request);
|
||||||
|
|
||||||
|
bool checkErrors(const std::shared_ptr<serversdk::Request> &request);
|
||||||
|
|
||||||
|
// Prepares request to be sent
|
||||||
|
std::shared_ptr<serversdk::Request> composeRequest(const QString &endpoint, const QByteArray &data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* @brief Signal which transfer data to Sync Manager
|
||||||
|
*/
|
||||||
|
void downloadDataFinished(std::shared_ptr<Data> = std::shared_ptr<Data>());
|
||||||
|
void dataProcessFinished(bool success = true);
|
||||||
|
|
||||||
|
private:
|
||||||
|
serversdk::RequestManager* mReqMan = nullptr;
|
||||||
|
QString mHost = "https://192.168.1.3:8443";
|
||||||
|
QString mUserName;
|
||||||
|
QString mPassword;
|
||||||
|
bool mDryMode = false;
|
||||||
|
bool mValid = true;
|
||||||
|
bool mSuccess = true;
|
||||||
|
|
||||||
|
QString authToken;
|
||||||
|
};
|
||||||
|
|
||||||
38
src/colnod/data.cpp
Normal file
38
src/colnod/data.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
bool Data::loadTokensFromJson(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (data.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto doc = QJsonDocument::fromJson(data);
|
||||||
|
auto items = doc.object()["items"].toArray();
|
||||||
|
for (auto item : items) {
|
||||||
|
auto token = std::make_shared<Token>();
|
||||||
|
doc.setObject(item.toObject());
|
||||||
|
token->fromJson(doc.toJson());
|
||||||
|
updatedTokens.insert(token->id(), token);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Data::loadSubjectsFormJson(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (data.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto doc = QJsonDocument::fromJson(data);
|
||||||
|
auto items = doc.object()["items"].toArray();
|
||||||
|
for (auto item : items) {
|
||||||
|
auto subject = std::make_shared<Subject>();
|
||||||
|
doc.setObject(item.toObject());
|
||||||
|
subject->fromJson(doc.toJson());
|
||||||
|
updatedSubjects.insert(subject->id(), subject);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Data::incrementCounter()
|
||||||
|
{
|
||||||
|
return ++counter;
|
||||||
|
}
|
||||||
72
src/colnod/data.h
Normal file
72
src/colnod/data.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "token.h"
|
||||||
|
#include "subject.h"
|
||||||
|
|
||||||
|
// constants for output design
|
||||||
|
constexpr auto equals = "==================================================================";
|
||||||
|
constexpr auto underscore = "__________________________________________________________________";
|
||||||
|
constexpr auto dash = "------------------------------------------------------------------";
|
||||||
|
constexpr auto tab = "\t";
|
||||||
|
|
||||||
|
|
||||||
|
class Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QString massageTemplate(const QString& action, const QString& item, const QString& source, const QString& itemInfo)
|
||||||
|
{
|
||||||
|
return action + " " + item + " in " + source + ", " + item + ": '" + itemInfo + "'";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Data
|
||||||
|
{
|
||||||
|
friend class ColnodAPI;
|
||||||
|
friend class DatabaseAPI;
|
||||||
|
public:
|
||||||
|
Data() = default;
|
||||||
|
/**
|
||||||
|
* @brief Fill data with tokens from JSON
|
||||||
|
* @param data Response data in JSON format
|
||||||
|
* @return True if success otherwise False
|
||||||
|
*/
|
||||||
|
bool loadTokensFromJson(const QByteArray &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill data with subjects from JSON
|
||||||
|
* @param data Response data in JSON format
|
||||||
|
* @return True if success otherwise False
|
||||||
|
*/
|
||||||
|
bool loadSubjectsFormJson(const QByteArray &data);
|
||||||
|
|
||||||
|
QHash<QString, std::shared_ptr<Token>> updatedTokens;
|
||||||
|
QList<QString> deletedTokens;
|
||||||
|
QHash<QString, std::shared_ptr<Subject>> updatedSubjects;
|
||||||
|
QList<QString> deletedSubjects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function for check if all endpoints calls are processed
|
||||||
|
* @return Returns acctual amout of processed endpoints
|
||||||
|
*/
|
||||||
|
int incrementCounter();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int counter = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PreparedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PreparedData() = default;
|
||||||
|
PreparedData(const std::shared_ptr<Data> &database, const std::shared_ptr<Data> &colnod)
|
||||||
|
:databaseData(database), colnodData(colnod)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make unique_ptr ( neeed because of ColnodAPI )
|
||||||
|
std::shared_ptr<Data> databaseData;
|
||||||
|
std::shared_ptr<Data> colnodData;
|
||||||
|
|
||||||
|
};
|
||||||
317
src/colnod/databaseapi.cpp
Normal file
317
src/colnod/databaseapi.cpp
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
#include "databaseapi.h"
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(database, "database.api");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DatabaseAPI::DatabaseAPI(serversdk::DatabaseManager *dbMan, bool dryMode)
|
||||||
|
: mDryMode(dryMode),
|
||||||
|
mDbMan(dbMan),
|
||||||
|
utils(std::make_unique<serversdk::DatabaseUtils>(dbMan))
|
||||||
|
{
|
||||||
|
mDbMan->setDryMode(dryMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::open(const QString& host, const QString& name, const QString& user, const QString& password)
|
||||||
|
{
|
||||||
|
return mDbMan->openPostgres(host, name, user, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::executeQuery(QSqlQuery &query, const QString &statement)
|
||||||
|
{
|
||||||
|
if (statement.isEmpty()) {
|
||||||
|
query = mDbMan->executeQuery(query);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query = mDbMan->executeQuery(query, statement);
|
||||||
|
}
|
||||||
|
if (!query.isActive()) {
|
||||||
|
this->mValid = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Data> DatabaseAPI::downloadData()
|
||||||
|
{
|
||||||
|
auto updatedQuery = mDbMan->createQuery();
|
||||||
|
auto deletedQuery = mDbMan->createQuery();
|
||||||
|
if (!executeQuery(updatedQuery, "SELECT * FROM colnod_token WHERE last_modified > last_update AND deleted = false")) {
|
||||||
|
qWarning(database) << "ERROR during geting new/updated tokens from db.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!executeQuery(deletedQuery, "SELECT id FROM colnod_token WHERE last_modified > last_update AND deleted = true")) {
|
||||||
|
qWarning(database) << "ERROR during geting marked tokens for deletion from db.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return composeData(updatedQuery, deletedQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::processData(const std::shared_ptr<Data> &data)
|
||||||
|
{
|
||||||
|
bool res = true;
|
||||||
|
if (!uploadTokens(data->updatedTokens)) {
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
if (!uploadSubjects(data->updatedSubjects)) {
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
if (!deleteItems(data->deletedTokens, "colnod_token")) {
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
if (!deleteItems(data->deletedSubjects, "colnod_subject")) {
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo(database) << tab << "- push db:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() <<
|
||||||
|
"] subjects [" << data->updatedSubjects.size() << ":" << data->deletedSubjects.size() << "] [ added/updated : deleted ]";
|
||||||
|
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
||||||
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
||||||
|
qInfo(database) << dash;
|
||||||
|
}
|
||||||
|
for (const auto &token : data->updatedTokens) {
|
||||||
|
qInfo(database) << Message::massageTemplate("Updated", "token", "database", token->name());
|
||||||
|
}
|
||||||
|
for (const auto &id : data->deletedTokens) {
|
||||||
|
qInfo(database) << Message::massageTemplate("Deleted", "token", "database", id);
|
||||||
|
}
|
||||||
|
for (const auto &subject : data->updatedSubjects) {
|
||||||
|
qInfo(database) << Message::massageTemplate("Updated", "subject", "database", subject->name());
|
||||||
|
}
|
||||||
|
for (const auto &id : data->deletedSubjects) {
|
||||||
|
qInfo(database) << Message::massageTemplate("Deleted", "subject", "database", id);
|
||||||
|
}
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
||||||
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
||||||
|
qInfo(database) << dash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save data to structure Data to be sent to syncManager
|
||||||
|
std::shared_ptr<Data> DatabaseAPI::composeData(const QSqlQuery &updatedQuery, QSqlQuery &deletedQuery)
|
||||||
|
{
|
||||||
|
auto data = std::make_shared<Data>();
|
||||||
|
auto list = utils->tableFromQuery<Token>(updatedQuery);
|
||||||
|
for (const auto &token : list) {
|
||||||
|
data->updatedTokens.insert(token->id(), token);
|
||||||
|
}
|
||||||
|
while (deletedQuery.next()) {
|
||||||
|
data->deletedTokens.append(deletedQuery.value(0).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo(database) << tab << "- src db:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() <<
|
||||||
|
"] [ added/updated : deleted ]";
|
||||||
|
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty()) {
|
||||||
|
qInfo(database) << dash;
|
||||||
|
}
|
||||||
|
for (const auto &token : data->updatedTokens) {
|
||||||
|
qInfo(database) << Message::massageTemplate("Download updated", "token", "database", token->name());
|
||||||
|
}
|
||||||
|
for (const auto &id : data->deletedTokens) {
|
||||||
|
qInfo(database) << Message::massageTemplate("Download deleted", "token", "database", id);
|
||||||
|
}
|
||||||
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty()) {
|
||||||
|
qInfo(database) << dash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time of last synchronization
|
||||||
|
qint64 DatabaseAPI::getLastSync()
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
if (!executeQuery(query, "SELECT last_sync FROM config")) {
|
||||||
|
qCritical(database) << "ERROR during getting time of last sync. Exit.";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (query.next()){
|
||||||
|
return query.value(0).toLongLong();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::setLastSync(qint64 timestamp)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
query.prepare("UPDATE config SET last_sync = :last_sync");
|
||||||
|
query.bindValue(":last_sync", timestamp);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qCritical(database) << "ERROR during set time of this synchronization as last sync time";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It update last_update time in db of deleted tokens, because in colnod is later time (time of real deletion from Colnod)
|
||||||
|
void DatabaseAPI::reupdateDeleted(const QList<QString>& ids)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
for (const auto &id : ids){
|
||||||
|
query.prepare("UPDATE colnod_token SET last_update = :last_update WHERE id = :id");
|
||||||
|
query.bindValue(":last_update", QDateTime::fromMSecsSinceEpoch(getNow()).toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
||||||
|
query.bindValue(":id", id);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qWarning(database) << "ERROR during updating last update of deleted token:" << id << ", it will be synced from Colnod to db in next sync. Last modified will be invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 DatabaseAPI::getNow()
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
if (!executeQuery(query, "SELECT EXTRACT(EPOCH FROM NOW())*1000")) {
|
||||||
|
qCritical(database) << "ERROR during getting acctual time from db";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
query.next();
|
||||||
|
return query.value(0).toLongLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
// All tokens which are pushed to Colnad are update back to db, because of diffrent last_update and init of new tokens
|
||||||
|
bool DatabaseAPI::uploadTokens(const QHash<QString, std::shared_ptr<Token>> &tokens, const QSet<QString> &filters)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
for (const auto &token : tokens) {
|
||||||
|
if (exists(token->id(), "colnod_token")) {
|
||||||
|
query = utils->updateTable(token.get(), "colnod_token", filters);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query = utils->insertIntoTable(token.get(), "colnod_token");
|
||||||
|
}
|
||||||
|
if (!query.isActive()) {
|
||||||
|
mValid = false;
|
||||||
|
qWarning(database) << "ERROR durning inserting/updating of token:" << token->name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::uploadSubjects(const QHash<QString, std::shared_ptr<Subject>> &subjects)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
for (const auto &subject : subjects) {
|
||||||
|
for (const auto &attribute : subject->getAttributes()) {
|
||||||
|
|
||||||
|
if (exists(attribute->id(), "colnod_subject_attribute")) {
|
||||||
|
query = utils->updateTable(attribute.get(), "colnod_subject_attribute");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query = utils->insertIntoTable(attribute.get(), "colnod_subject_attribute");
|
||||||
|
}
|
||||||
|
if (!query.isActive()) {
|
||||||
|
mValid = false;
|
||||||
|
qWarning(database) << "ERROR durning inserting/updating of subject attribute:" << attribute->attributeValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exists(subject->id(), "colnod_subject")) {
|
||||||
|
query = utils->updateTable(subject.get(), "colnod_subject");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query = utils->insertIntoTable(subject.get(), "colnod_subject");
|
||||||
|
}
|
||||||
|
if (!query.isActive()) {
|
||||||
|
mValid = false;
|
||||||
|
qWarning(database) << "ERROR durning inserting/updating of subject attribute:" << subject->name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::deleteItems(const QList<QString> &ids, const QString &tableName)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
QString nowString = QDateTime::fromMSecsSinceEpoch(getLastSync()).toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||||
|
|
||||||
|
if (tableName == "colnod_subject") {
|
||||||
|
for (const auto &id : ids) {
|
||||||
|
query.prepare("DELETE FROM colnod_subject_attribute WHERE subject_id = :id");
|
||||||
|
query.bindValue(":id", id);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qWarning(database) << "ERROR during marking as deleted:" << id;
|
||||||
|
mValid = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &id : ids) {
|
||||||
|
query.prepare(QString("UPDATE %1 SET deleted = true, last_update = :now, last_modified = :now WHERE id = :id").arg(tableName));
|
||||||
|
query.bindValue(":id", id);
|
||||||
|
query.bindValue(":now", nowString);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qWarning(database) << "ERROR during marking as deleted:" << id;
|
||||||
|
mValid = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Token> DatabaseAPI::getToken(const QString &id)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
query.prepare("SELECT * FROM colnod_token WHERE id = :id");
|
||||||
|
query.bindValue(":id", id);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qCritical(database) << "ERROR during accessing existing tokens. EXIT";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto list = utils->tableFromQuery<Token>(query);
|
||||||
|
if (list.isEmpty())
|
||||||
|
return std::make_shared<Token>();
|
||||||
|
return list.at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseAPI::isValid()
|
||||||
|
{
|
||||||
|
return mValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatabaseAPI::exists(const QString &id, const QString &tableName)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
query.prepare(QString("SELECT 1 FROM %1 WHERE id = :id").arg(tableName));
|
||||||
|
query.bindValue(":id", id);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qCritical(database) << "ERROR during accessing existing tokens. EXIT(1)";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return query.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint16 DatabaseAPI::getNumOfTokensOfSubject(const QString &id)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
query.prepare("SELECT 1 FROM colnod_token WHERE subject_id = :id AND description = 'mobile'");
|
||||||
|
query.bindValue(":id", id);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qCritical(database) << "ERROR during accessing existing tokens. EXIT(1)";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return static_cast<qint16>(query.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseAPI::getSubjectNameByTokenId(const QString &tokenId)
|
||||||
|
{
|
||||||
|
auto query = mDbMan->createQuery();
|
||||||
|
query.prepare("SELECT name FROM colnod_subject WHERE id = (SELECT subject_id FROM colnod_token WHERE id = :tokenId)");
|
||||||
|
query.bindValue(":tokenId", tokenId);
|
||||||
|
if (!executeQuery(query)) {
|
||||||
|
qCritical(database) << "ERROR during accessing existing subject. EXIT(1)";
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
if (query.next())
|
||||||
|
return query.value(0).toString();
|
||||||
|
qWarning() << "Token:" << tokenId << "is not assigned to any subject";
|
||||||
|
return "(No subject)";
|
||||||
|
}
|
||||||
128
src/colnod/databaseapi.h
Normal file
128
src/colnod/databaseapi.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSql>
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
#include "serversdk/databasemanager.h"
|
||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
#include "serversdk/databaseutils.h"
|
||||||
|
#include "serversdk/logger.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(database)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAPI : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DatabaseAPI(serversdk::DatabaseManager *dbMan, bool dryMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connecting to database
|
||||||
|
* @param host
|
||||||
|
* @param name
|
||||||
|
* @param user
|
||||||
|
* @param password
|
||||||
|
* @return True if success False otherwise
|
||||||
|
*/
|
||||||
|
bool open(const QString& host = "192.168.1.94",
|
||||||
|
const QString& name = "deloitte",
|
||||||
|
const QString& user = "postgres",
|
||||||
|
const QString& password = "34rjkciea12");
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<Data> downloadData();
|
||||||
|
bool processData(const std::shared_ptr<Data> &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get time of last synchronization
|
||||||
|
*
|
||||||
|
* LastSync time is stored in database, config table
|
||||||
|
*/
|
||||||
|
qint64 getLastSync();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set time of acctual synchronization
|
||||||
|
* @param timestamp
|
||||||
|
*/
|
||||||
|
bool setLastSync(qint64 timestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update time of tokens which were deleted in actual sync
|
||||||
|
* @param ids Ids of deleted items
|
||||||
|
*/
|
||||||
|
void reupdateDeleted(const QList<QString>& ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get time from database server
|
||||||
|
* @return Time in miliseconds
|
||||||
|
*/
|
||||||
|
qint64 getNow();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push tokens prepared in this sync to database
|
||||||
|
*
|
||||||
|
* @param tokens Tokens to be pushed to database
|
||||||
|
* @param filters Can be used to omit tokens properties (Properties in filters won't be pushed to database
|
||||||
|
*
|
||||||
|
* @return True if success, False otherwise
|
||||||
|
*/
|
||||||
|
bool uploadTokens(const QHash<QString, std::shared_ptr<Token> > &tokens, const QSet<QString> &filters = {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push subjects prepared in this sync to database
|
||||||
|
*
|
||||||
|
* @param subjects Subjects prepared to be pushed to database
|
||||||
|
*
|
||||||
|
* @return True if success, False otherwise
|
||||||
|
*/
|
||||||
|
bool uploadSubjects(const QHash<QString, std::shared_ptr<Subject> > &subjects);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set deleted of tokens/subjects to true
|
||||||
|
*
|
||||||
|
* @param ids Items which was prepared to be deleted from database
|
||||||
|
* @param tableName Name of table with subjects or tokens
|
||||||
|
*
|
||||||
|
* @return True if success, False otherwise
|
||||||
|
*/
|
||||||
|
bool deleteItems(const QList<QString> &ids, const QString &tableName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get token from database
|
||||||
|
*
|
||||||
|
* @param id Id of token accessing
|
||||||
|
*
|
||||||
|
* @return Return token
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Token> getToken(const QString &id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if this sync was without any problem
|
||||||
|
*/
|
||||||
|
bool isValid();
|
||||||
|
|
||||||
|
// Helper functions for naming new tokens
|
||||||
|
qint16 getNumOfTokensOfSubject(const QString &id);
|
||||||
|
QString getSubjectNameByTokenId(const QString &tokenId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int exists(const QString &id, const QString &tableName);
|
||||||
|
bool executeQuery(QSqlQuery &query, const QString &statement = QString());
|
||||||
|
|
||||||
|
std::shared_ptr<Data> composeData(const QSqlQuery &updatedQuery, QSqlQuery &deletedQuery);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void downloadDataFinished(std::shared_ptr<Data> = std::shared_ptr<Data>());
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mValid = true;
|
||||||
|
|
||||||
|
bool mDryMode;
|
||||||
|
serversdk::DatabaseManager *mDbMan = nullptr;
|
||||||
|
std::unique_ptr<serversdk::DatabaseUtils> utils;
|
||||||
|
};
|
||||||
@ -1,322 +0,0 @@
|
|||||||
#include "databasemanager.h"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
|
|
||||||
DatabaseManager::DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, const bool &dryMode)
|
|
||||||
:dryMode(dryMode)
|
|
||||||
{
|
|
||||||
db = QSqlDatabase::addDatabase("QPSQL");
|
|
||||||
db.setDatabaseName(dbName);
|
|
||||||
db.setHostName(host);
|
|
||||||
db.setUserName(user); // home-office stuff -->> db.setPort(80);
|
|
||||||
db.setPassword(password);
|
|
||||||
if (!db.open())
|
|
||||||
{
|
|
||||||
opened = false;
|
|
||||||
Logger::log("Opening error" + db.lastError().text());
|
|
||||||
emit error();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
opened = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add or update subject to db
|
|
||||||
int DatabaseManager::saveSubject(const std::shared_ptr<Subject>& subject, const QString& table)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
QString id = subject->idToStringWithoutBraces();
|
|
||||||
int rtn;
|
|
||||||
if (checkIfExists(id, table)){
|
|
||||||
query.prepare(QString("UPDATE %1 SET id = :id, description = :description, last_update = :last_update, name = :name, pin = :pin, last_modified = :last_modified, deleted = :deleted "
|
|
||||||
"WHERE id = :id").arg(table));
|
|
||||||
rtn = -1;
|
|
||||||
}else{
|
|
||||||
query.prepare(QString("INSERT INTO %1 (id, description, last_update, name, pin, last_modified, deleted) "
|
|
||||||
"VALUES (:id, :description, :last_update, :name, :pin, :last_modified, :deleted)").arg(table));
|
|
||||||
rtn = 1;
|
|
||||||
}
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
query.bindValue(":description", subject->getDescription());
|
|
||||||
query.bindValue(":name", subject->getName());
|
|
||||||
query.bindValue(":last_update", QDateTime::fromMSecsSinceEpoch(subject->getLastUpdate()).toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
|
||||||
query.bindValue(":pin", subject->getPin());
|
|
||||||
query.bindValue(":last_modified", QDateTime::fromMSecsSinceEpoch(subject->getLastUpdate()).toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
|
||||||
query.bindValue(":deleted", subject->getDeleted());
|
|
||||||
executeQuery(query);
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add or update token to DB
|
|
||||||
int DatabaseManager::saveToken(const std::shared_ptr<Token>& token, const QString& table)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
QString id = token->idToStringWithoutBraces();
|
|
||||||
int rtn;
|
|
||||||
if (checkIfExists(id, table)){
|
|
||||||
query.prepare(QString("UPDATE %1 SET id = :id, auth_errors = :auth_errors, description = :description, enabled = :enabled, last_update = :last_update, name = :name, no_validity = :no_validity, time_to_live = :time_to_live, token_bits = :token_bits, token_data = :token_data, token_type = :token_type, subject_id = :subject_id, last_modified = :last_modified, deleted = :deleted "
|
|
||||||
"WHERE id = :id").arg(table));
|
|
||||||
rtn = -1;
|
|
||||||
}else{
|
|
||||||
query.prepare(QString("INSERT INTO %1 (id, auth_errors, description, enabled, last_update, name, no_validity, time_to_live, token_bits, token_data, token_type, subject_id, last_modified, deleted) "
|
|
||||||
"VALUES (:id, :auth_errors, :description, :enabled, :last_update, :name, :no_validity, :time_to_live, :token_bits, :token_data, :token_type, :subject_id, :last_modified, :deleted)").arg(table));
|
|
||||||
rtn = 1;
|
|
||||||
}
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
query.bindValue(":auth_errors", token->getAuthErrors());
|
|
||||||
query.bindValue(":description", token->getDescription());
|
|
||||||
query.bindValue(":enabled", token->getFlags().contains("Enabled"));
|
|
||||||
query.bindValue(":last_update", QDateTime::fromMSecsSinceEpoch(token->getLastUpdate()).toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
|
||||||
query.bindValue(":name", token->getName());
|
|
||||||
query.bindValue(":no_validity", token->getFlags().contains("NoValidity"));
|
|
||||||
query.bindValue(":time_to_live", token->getTimeToLive());
|
|
||||||
query.bindValue(":token_bits", token->getTokenBits());
|
|
||||||
query.bindValue(":token_data", token->getTokenData());
|
|
||||||
query.bindValue(":token_type", token->getTokenType());
|
|
||||||
query.bindValue(":subject_id", token->subjectIdToStringWithoutBraces());
|
|
||||||
query.bindValue(":last_modified", QDateTime::fromMSecsSinceEpoch(token->getLastUpdate()).toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
|
||||||
query.bindValue(":deleted", token->getDeleted());
|
|
||||||
executeQuery(query);
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add of update subject attributes
|
|
||||||
void DatabaseManager::saveSubjectAttributes(const std::shared_ptr<Subject>& subject, const QString& table)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
auto attributes = subject->getAtr();
|
|
||||||
for (const auto& attribute : attributes){
|
|
||||||
QString id = subject->idToStringWithoutBraces() + " " + attribute.first;
|
|
||||||
if (checkIfExists(id, table)){
|
|
||||||
query.prepare(QString("UPDATE %1 SET id = :id, attribute_key = :key, attribute_value = :value, subject_id = :subject_id) "
|
|
||||||
"WHERE id = :id").arg(table));
|
|
||||||
}else{
|
|
||||||
query.prepare(QString("INSERT INTO %1 (id, attribute_key, attribute_value, subject_id)"
|
|
||||||
"VALUES (:id, :key, :value, :subject_id)").arg(table));
|
|
||||||
}
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
query.bindValue(":key", attribute.first);
|
|
||||||
query.bindValue(":value", attribute.second);
|
|
||||||
query.bindValue(":subject_id", subject->idToStringWithoutBraces());
|
|
||||||
executeQuery(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "delete" tokens from DB -> set deleted true
|
|
||||||
void DatabaseManager::setDeleted(const QString& id, const QString& table, quint64 time)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
QString nowString = QDateTime::fromMSecsSinceEpoch(time).toString("yyyy-MM-dd hh:mm:ss.zzz");
|
|
||||||
query.prepare(QString("UPDATE %1 SET deleted = true, last_update = :now, last_modified = :now WHERE id = :id").arg(table));
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
query.bindValue(":now", nowString);
|
|
||||||
executeQuery(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatabaseManager::checkIfExists(const QString& id, const QString& table)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare(QString("SELECT EXISTS(SELECT 1 FROM %1 WHERE id = :id)").arg(table));
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
executeQuery(query);
|
|
||||||
query.first();
|
|
||||||
return query.value(0).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatabaseManager::executeQuery(QSqlQuery &query, const QString &statement)
|
|
||||||
{
|
|
||||||
if (statement.isEmpty()){
|
|
||||||
if (dryMode){
|
|
||||||
QString sqlStatement = getLastQuery(query);
|
|
||||||
qDebug() << sqlStatement;
|
|
||||||
}
|
|
||||||
if (!dryMode || getLastQuery(query).startsWith("SELECT")){
|
|
||||||
if (!query.exec()){
|
|
||||||
QString msg = QString("Source: Database Manager, Error: %1").arg(query.lastError().text());
|
|
||||||
Logger::logToDb(QString(msg));
|
|
||||||
emit error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (dryMode){
|
|
||||||
qDebug() << statement;
|
|
||||||
}
|
|
||||||
if (!dryMode || statement.startsWith("SELECT")){
|
|
||||||
if (!query.exec(statement)){
|
|
||||||
QString msg = QString("Source: Database Manager, Error: %1").arg(query.lastError().text());
|
|
||||||
Logger::logToDb(QString(msg));
|
|
||||||
emit error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatabaseManager::dropTables()
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
|
|
||||||
executeQuery(query, "DROP TABLE IF EXISTS colnod_token, colnod_subject, colnod_subject_attribute");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create table after deleting, it is used for full sync
|
|
||||||
void DatabaseManager::createTables()
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
|
|
||||||
executeQuery(query, "CREATE TABLE colnod_subject ("
|
|
||||||
"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)");
|
|
||||||
executeQuery(query, "CREATE TABLE colnod_token ("
|
|
||||||
"id CHAR(36) primary key NOT NULL,"
|
|
||||||
"auth_errors INTEGER DEFAULT 255,"
|
|
||||||
"description VARCHAR(255),"
|
|
||||||
"enabled BOOLEAN DEFAULT true,"
|
|
||||||
"last_update timestamp DEFAULT '1970-01-01 00:00:00'::timestamp without time zone,"
|
|
||||||
"name VARCHAR(255),"
|
|
||||||
"no_validity BOOLEAN DEFAULT true,"
|
|
||||||
"time_to_live INTEGER DEFAULT 255,"
|
|
||||||
"token_bits INTEGER DEFAULT 32,"
|
|
||||||
"token_data VARCHAR(255),"
|
|
||||||
"token_type VARCHAR(255) DEFAULT 'Token'::character varying,"
|
|
||||||
"subject_id CHAR(36),"
|
|
||||||
"last_modified timestamp NOT NULL,"
|
|
||||||
"deleted BOOLEAN DEFAULT false)");
|
|
||||||
executeQuery(query, "CREATE TABLE colnod_subject_attribute ("
|
|
||||||
"id VARCHAR(255) primary key,"
|
|
||||||
"attribute_key VARCHAR(255),"
|
|
||||||
"attribute_value VARCHAR(255),"
|
|
||||||
"subject_id CHAR(36))");
|
|
||||||
}
|
|
||||||
|
|
||||||
// return last_sync time
|
|
||||||
quint64 DatabaseManager::getLastSync()
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
executeQuery(query, "SELECT last_sync FROM config");
|
|
||||||
query.next();
|
|
||||||
quint64 temp = query.value(0).toULongLong();
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on the very end of sync set new Last_sync time (begin of this sync)
|
|
||||||
void DatabaseManager::updateLastSync(quint64 lastSync)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("UPDATE config SET last_sync = :last_sync");
|
|
||||||
query.bindValue(":last_sync", lastSync);
|
|
||||||
executeQuery(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// return set of tokens to update form DB
|
|
||||||
QSqlQuery DatabaseManager::getUpdatedTokens()
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
executeQuery(query, "SELECT id, auth_errors, description, enabled, last_update, name, no_validity, time_to_live, token_bits, token_data, token_type, subject_id, last_modified, deleted "
|
|
||||||
"FROM colnod_token WHERE EXTRACT(EPOCH FROM last_modified) * 1000 > EXTRACT(EPOCH FROM last_update) * 1000");
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSqlQuery DatabaseManager::getToken(const QString& id)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("SELECT id, auth_errors, description, enabled, last_update, name, no_validity, time_to_live, token_bits, token_data, token_type, subject_id, last_modified, deleted "
|
|
||||||
"FROM colnod_token WHERE id = :id");
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
executeQuery(query);
|
|
||||||
query.next();
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 DatabaseManager::getNow()
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
executeQuery(query, "SELECT EXTRACT(EPOCH FROM NOW())*1000");
|
|
||||||
query.next();
|
|
||||||
return query.value(0).toULongLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync last update back to DB from Colnod after changes made to Colnod
|
|
||||||
void DatabaseManager::setLastUpdate(const QString& id, quint64 lastUpdate)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
QString lsatUpdateString = QDateTime::fromMSecsSinceEpoch(lastUpdate).toString("yyyy-MM-dd hh:mm:ss.zzz");
|
|
||||||
query.prepare("UPDATE colnod_token SET last_update = :last_update WHERE id = :id");
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
query.bindValue(":last_update", lsatUpdateString);
|
|
||||||
executeQuery(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatabaseManager::log(const QString &msg)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("INSERT INTO logs (log_time, msg) VALUES (NOW(), :msg)");
|
|
||||||
query.bindValue(":msg", msg);
|
|
||||||
query.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString DatabaseManager::getSubjectNameByTokenId(const QString &tokenId)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("SELECT name FROM colnod_subject WHERE id = (SELECT subject_id FROM colnod_token WHERE id = :tokenId)");
|
|
||||||
query.bindValue(":tokenId", tokenId);
|
|
||||||
executeQuery(query);
|
|
||||||
if (query.next())
|
|
||||||
return query.value(0).toString();
|
|
||||||
qWarning() << "Token:" << tokenId << "is not assigned to any subject";
|
|
||||||
return "(No subject)";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString DatabaseManager::getSubjectNameById(const QString &id)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("SELECT name FROM colnod_subject WHERE id = :id");
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
executeQuery(query);
|
|
||||||
if (query.next())
|
|
||||||
return query.value(0).toString();
|
|
||||||
qWarning() << "Subject:" << id << "doesn't exists";
|
|
||||||
return "(No subject)";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatabaseManager::checkDeletedToken(const QString &id)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("SELECT deleted FROM colnod_token WHERE id = :id");
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
executeQuery(query);
|
|
||||||
query.next();
|
|
||||||
return query.value(0).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
qint16 DatabaseManager::getNumOfTokensOfSubject(const QString &id)
|
|
||||||
{
|
|
||||||
QSqlQuery query;
|
|
||||||
query.prepare("SELECT 1 FROM colnod_token WHERE subject_id = :id AND deleted = false");
|
|
||||||
query.bindValue(":id", id);
|
|
||||||
executeQuery(query);
|
|
||||||
return static_cast<qint16>(query.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatabaseManager::isOpen()
|
|
||||||
{
|
|
||||||
return opened;
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QSql>
|
|
||||||
#include <QSqlDatabase>
|
|
||||||
#include <QSqlQuery>
|
|
||||||
#include <QSqlError>
|
|
||||||
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "subject.h"
|
|
||||||
#include "token.h"
|
|
||||||
#include <QDateTime>
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManager : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
DatabaseManager(const QString& host, const QString& dbName, const QString& user, const QString& password, const bool& dryMode = false);
|
|
||||||
|
|
||||||
int saveSubject(const std::shared_ptr<Subject>& subject, const QString& table);
|
|
||||||
int saveToken(const std::shared_ptr<Token>& token, const QString& table);
|
|
||||||
void saveSubjectAttributes(const std::shared_ptr<Subject>& subject, const QString& table);
|
|
||||||
void setDeleted(const QString& id, const QString& table, quint64 time);
|
|
||||||
|
|
||||||
void dropTables();
|
|
||||||
void createTables();
|
|
||||||
|
|
||||||
quint64 getLastSync();
|
|
||||||
void updateLastSync(quint64 lastSync);
|
|
||||||
quint64 getNow();
|
|
||||||
|
|
||||||
QSqlQuery getUpdatedTokens();
|
|
||||||
QSqlQuery getToken(const QString& id);
|
|
||||||
|
|
||||||
void setLastUpdate(const QString& id, quint64 lastUpdate);
|
|
||||||
|
|
||||||
void log(const QString &msg);
|
|
||||||
QString getSubjectNameByTokenId(const QString &tokenId);
|
|
||||||
QString getSubjectNameById(const QString &id);
|
|
||||||
bool checkDeletedToken(const QString &id);
|
|
||||||
|
|
||||||
qint16 getNumOfTokensOfSubject(const QString &id);
|
|
||||||
|
|
||||||
bool isOpen();
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool checkIfExists(const QString& id, const QString& table);
|
|
||||||
bool executeQuery(QSqlQuery &query, const QString &statement = QString());
|
|
||||||
QString getLastQuery(const QSqlQuery &query) const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void error();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QSqlDatabase db;
|
|
||||||
bool dryMode = false;
|
|
||||||
bool opened = false;
|
|
||||||
};
|
|
||||||
@ -1,372 +0,0 @@
|
|||||||
#include "datamanager.h"
|
|
||||||
|
|
||||||
DataManager::DataManager(const bool &dryMode) :
|
|
||||||
db(std::make_shared<DatabaseManager>("192.168.1.94", "deloitte", "postgres", "34rjkciea12", dryMode)),
|
|
||||||
dryMode(dryMode)
|
|
||||||
{
|
|
||||||
if (db->isOpen()){
|
|
||||||
Logger::setDBMan(db);
|
|
||||||
nextLastSync = db->getNow();
|
|
||||||
lastSync = db->getLastSync();
|
|
||||||
}
|
|
||||||
connect(db.get(), &DatabaseManager::error, this, &DataManager::error, Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load all tokens from colnod to comparing with working set from DB
|
|
||||||
void DataManager::fillFullTokensColnod(const QString& response)
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
|
||||||
QJsonArray items = doc.object()["items"].toArray();
|
|
||||||
if (!items.empty())
|
|
||||||
{
|
|
||||||
for (QJsonValueRef item : items){
|
|
||||||
auto token = std::make_shared<Token>();
|
|
||||||
token->fromJSON(item.toObject());
|
|
||||||
allTokensFromColnodById.insert(token->idToStringWithoutBraces(), token);
|
|
||||||
allTokensFromColnodByName.insert(token->getName(), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
qCritical() << "Colnod is Empty";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load working set with updated tokens from Colnod
|
|
||||||
void DataManager::fillTokensToUpdateColnod(const QString& response)
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
|
||||||
QJsonArray items = doc.object()["items"].toArray();
|
|
||||||
if (!items.empty())
|
|
||||||
{
|
|
||||||
for (QJsonValueRef item : items)
|
|
||||||
{
|
|
||||||
auto token = std::make_shared<Token>();
|
|
||||||
token->fromJSON(item.toObject());
|
|
||||||
token->setDeleted(false);
|
|
||||||
tokensToUpdateFromColnodToDB.insert(token->idToStringWithoutBraces(), token);
|
|
||||||
}
|
|
||||||
qInfo() << "Colnod:" << tokensToUpdateFromColnodToDB.size() << "tokens to update";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qInfo() << "Colnod: 0 tokens to update";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load working set with updated subjects from Colnod
|
|
||||||
void DataManager::fillSubjectsToUpdateColnod(const QString& response)
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
|
||||||
QJsonArray items = doc.object()["items"].toArray();
|
|
||||||
if (!items.empty())
|
|
||||||
{
|
|
||||||
for (QJsonValueRef item : items)
|
|
||||||
{
|
|
||||||
auto subject = std::make_shared<Subject>();
|
|
||||||
subject->fromJSON(item.toObject());
|
|
||||||
subject->setDeleted(false);
|
|
||||||
subjectsToUpdateFromColnodToDB.insert(subject->idToStringWithoutBraces(), subject);
|
|
||||||
}
|
|
||||||
qInfo() << "Colnod:" << subjectsToUpdateFromColnodToDB.size() << "subjects to update";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qInfo() << "Colnod:" << "0 subjects to update";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load working sets with updated/deleted tokens from DB
|
|
||||||
void DataManager::fillTokensToChangeDB()
|
|
||||||
{
|
|
||||||
if (db->isOpen()){
|
|
||||||
QSqlQuery query = db->getUpdatedTokens();
|
|
||||||
while (query.next())
|
|
||||||
{
|
|
||||||
auto token = std::make_shared<Token>();
|
|
||||||
token->fromSQLRecord(query);
|
|
||||||
if (token->getName() == NULL){
|
|
||||||
qint16 num = db->getNumOfTokensOfSubject(token->subjectIdToStringWithoutBraces());
|
|
||||||
token->setName(db->getSubjectNameByTokenId(token->idToStringWithoutBraces()) + "_mobile_" + QString::number(num + 1));
|
|
||||||
token->setDescription("mobile");
|
|
||||||
token->setLastUpdate(token->getLastModified());
|
|
||||||
db->saveToken(token, "colnod_token");
|
|
||||||
}
|
|
||||||
if (token->getDeleted()){ // sort to delete / to update
|
|
||||||
tokensFromDBToDeleteFromColnod.append(token->idToStringWithoutBraces());
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
tokensToUpdateFromDBToColnod.insert(token->idToStringWithoutBraces(), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qDebug() << "DB:" << tokensToUpdateFromDBToColnod.size() << "tokens to update";
|
|
||||||
qDebug() << "DB:" << tokensFromDBToDeleteFromColnod.size() << "tokens to delete";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
emit error();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataManager::fillSubjectsToUpdateDB()
|
|
||||||
{
|
|
||||||
// Not needed yet
|
|
||||||
}
|
|
||||||
|
|
||||||
// load working set with subjest to delete from Colnod
|
|
||||||
void DataManager::fillSubjectsToDeleteColnod(const QString& json)
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QJsonArray items = doc.object()["items"].toArray();
|
|
||||||
if (!items.empty())
|
|
||||||
{
|
|
||||||
for (QJsonValueRef item : items) {
|
|
||||||
subjectsFromColnodToDeleteFromDB.append(item.toString());
|
|
||||||
}
|
|
||||||
qInfo() << "Colnod:" << subjectsFromColnodToDeleteFromDB.size() << "subjects to delete";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qInfo() << "Colnod: 0 subjects to delete";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load working set with tokens to delete from DB
|
|
||||||
void DataManager::fillTokensToDeleteColnod(const QString& json)
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QJsonArray items = doc.object()["items"].toArray();
|
|
||||||
if (!items.empty())
|
|
||||||
{
|
|
||||||
for (QJsonValueRef item : items) {
|
|
||||||
tokensFromColnodToDeleteFromDB.append(item.toString());
|
|
||||||
}
|
|
||||||
qInfo() << "Colnod:" << tokensFromColnodToDeleteFromDB.size() << "tokens to delete";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qInfo() << "Colnod: 0 tokens to delete";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataManager::fillSubjectsToDeleteDB()
|
|
||||||
{
|
|
||||||
// Not needed yet
|
|
||||||
}
|
|
||||||
|
|
||||||
// update subjects from colnod to DB
|
|
||||||
void DataManager::pushSubjectsToDB()
|
|
||||||
{
|
|
||||||
if (subjectsToUpdateFromColnodToDB.isEmpty()) {
|
|
||||||
qInfo() << "DB: 0 subjects added";
|
|
||||||
qInfo() << "DB: 0 subjects updated";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int updated = 0;
|
|
||||||
int added = 0;
|
|
||||||
for (const auto& subject : subjectsToUpdateFromColnodToDB)
|
|
||||||
{
|
|
||||||
if (db->saveSubject(subject, "colnod_subject") > 0){
|
|
||||||
added++;
|
|
||||||
if (dryMode)
|
|
||||||
qInfo() << "Adding subject:" << subject->idToStringWithoutBraces() << "to DB";
|
|
||||||
else
|
|
||||||
Logger::logToDb(Logger::makeSubjectMsg( subject->idToStringWithoutBraces(),"added", "DB"));
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
updated++;
|
|
||||||
if (dryMode)
|
|
||||||
qInfo() << "Updating subject:" << subject->idToStringWithoutBraces() << "to DB";
|
|
||||||
else
|
|
||||||
Logger::logToDb(Logger::makeSubjectMsg( subject->idToStringWithoutBraces(),"updated", "DB"));
|
|
||||||
}
|
|
||||||
db->saveSubjectAttributes(subject, "colnod_subject_attribute");
|
|
||||||
}
|
|
||||||
qInfo() << "DB:" << added << "subjects added";
|
|
||||||
qInfo() << "DB:" << updated << "subjects updated";
|
|
||||||
}
|
|
||||||
|
|
||||||
// update tokens from colnod to DB
|
|
||||||
void DataManager::pushTokensToDB()
|
|
||||||
{
|
|
||||||
if (tokensToUpdateFromColnodToDB.isEmpty()){
|
|
||||||
qInfo() << "DB: 0 tokens added";
|
|
||||||
qInfo() << "DB: 0 tokens updated";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
filterTokensColnod();
|
|
||||||
int updated = 0;
|
|
||||||
int added = 0;
|
|
||||||
for (const auto& token : tokensToUpdateFromColnodToDB)
|
|
||||||
{
|
|
||||||
|
|
||||||
QString text;
|
|
||||||
// 1 = adding token
|
|
||||||
// -1 = updating token
|
|
||||||
if (db->saveToken(token, "colnod_token") == 1){
|
|
||||||
added++;
|
|
||||||
text = "Added";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
updated++;
|
|
||||||
text = "Updating";
|
|
||||||
}
|
|
||||||
if (dryMode)
|
|
||||||
qInfo() << text << "token:" << token->idToStringWithoutBraces() << "to DB";
|
|
||||||
else
|
|
||||||
Logger::logToDb(Logger::makeTokenMsg(token->idToStringWithoutBraces(), text, "DB"));
|
|
||||||
}
|
|
||||||
qInfo() << "DB:" << added << "tokens added/edited";
|
|
||||||
qInfo() << "DB:" << updated << "tokens updated";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataManager::deleteSubjectsFromDB()
|
|
||||||
{
|
|
||||||
if (subjectsFromColnodToDeleteFromDB.isEmpty())
|
|
||||||
{
|
|
||||||
qInfo() << "DB: 0 subjects deleted";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const auto& subjectId : subjectsFromColnodToDeleteFromDB)
|
|
||||||
{
|
|
||||||
db->setDeleted(subjectId, "colnod_subject", nextLastSync);
|
|
||||||
if (dryMode)
|
|
||||||
qInfo() << "Deleting subject:" << subjectId << "from DB";
|
|
||||||
else
|
|
||||||
Logger::logToDb(Logger::makeSubjectMsg(subjectId, "deleted", "DB"));
|
|
||||||
}
|
|
||||||
qInfo() << "DB:" << subjectsFromColnodToDeleteFromDB.size() << " subject deleted";
|
|
||||||
}
|
|
||||||
|
|
||||||
// "delete" (set deleted true) tokens in DB depends on Colnod
|
|
||||||
void DataManager::setDeletedToTokensFromDB()
|
|
||||||
{
|
|
||||||
if (tokensFromColnodToDeleteFromDB.isEmpty())
|
|
||||||
{
|
|
||||||
qDebug() << "DB: 0 tokens deleted";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int alreadyDeleted = 0;
|
|
||||||
for (const auto& tokenId : tokensFromColnodToDeleteFromDB)
|
|
||||||
{
|
|
||||||
if (!db->checkDeletedToken(tokenId)){
|
|
||||||
db->setDeleted(tokenId, "colnod_token", nextLastSync);
|
|
||||||
if (dryMode)
|
|
||||||
qInfo() << "Deleting token:" << tokenId << "from DB";
|
|
||||||
else
|
|
||||||
Logger::logToDb(Logger::makeTokenMsg(tokenId, "deleted", "DB"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
alreadyDeleted++;
|
|
||||||
}
|
|
||||||
qDebug() << "DB:" << tokensFromColnodToDeleteFromDB.size() - alreadyDeleted << "tokens deleted";
|
|
||||||
}
|
|
||||||
|
|
||||||
// recreate working tables
|
|
||||||
void DataManager::dropTables()
|
|
||||||
{
|
|
||||||
if (db->isOpen()){
|
|
||||||
db->dropTables();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
emit error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DataManager::createTables()
|
|
||||||
{
|
|
||||||
if (db->isOpen()){
|
|
||||||
db->createTables();
|
|
||||||
db->updateLastSync(0);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
emit error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 DataManager::getLastModifiedOfToken(const QString &id)
|
|
||||||
{
|
|
||||||
auto record = db->getToken(id);
|
|
||||||
auto token = std::make_shared<Token>();
|
|
||||||
token->fromSQLRecord(record);
|
|
||||||
return token->getLastModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
//clear all structures
|
|
||||||
void DataManager::clear()
|
|
||||||
{
|
|
||||||
subjectsToUpdateFromColnodToDB.clear();
|
|
||||||
subjectsFromColnodToDeleteFromDB.clear();
|
|
||||||
tokensToUpdateFromColnodToDB.clear();
|
|
||||||
tokensFromColnodToDeleteFromDB.clear();
|
|
||||||
//subjectsDB.clear(); // not need yet
|
|
||||||
// subjectsToDeleteDB.clear(); // not need yet
|
|
||||||
tokensToUpdateFromDBToColnod.clear();
|
|
||||||
tokensFromDBToDeleteFromColnod.clear();
|
|
||||||
allTokensFromColnodById.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// on the very end of sync update last sync time to begin time of this sync
|
|
||||||
void DataManager::updateLastSync()
|
|
||||||
{
|
|
||||||
if (db->isOpen())
|
|
||||||
db->updateLastSync(nextLastSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
// these to filter functions solve colision tokens -> remove when change on this token is earlier then change on the same token on the other side (Colnod/DB)
|
|
||||||
void DataManager::filterTokensDB()
|
|
||||||
{
|
|
||||||
QList<QString> idsToDelete;
|
|
||||||
for (const auto& token : tokensToUpdateFromDBToColnod)
|
|
||||||
{
|
|
||||||
QString id = token->idToStringWithoutBraces();
|
|
||||||
Q_ASSERT(token != allTokensFromColnodById.value(id)); // if working sets are loading correctly equels should never happend
|
|
||||||
if (allTokensFromColnodById.contains(id) && *token < *allTokensFromColnodById.value(id))
|
|
||||||
idsToDelete.append(id);
|
|
||||||
if (allTokensFromColnodById.contains(id) && token->getLastModified() == allTokensFromColnodById.value(id)->getLastUpdate()){
|
|
||||||
idsToDelete.append(id);
|
|
||||||
qWarning() << "Same token has been edited in colnod and db in same time. Change from Colnod has been used";
|
|
||||||
}
|
|
||||||
if (allTokensFromColnodByName.contains(token->getName())){
|
|
||||||
qWarning() << "Token:" << token->idToStringWithoutBraces() << "won't be added to Colnod, because token with the same name already exists, please change it manually";
|
|
||||||
if (!dryMode){
|
|
||||||
QString message = QString("Error: Token: %1 won't be added to Colnod, because token with the same name already exists, please change it manually").arg(token->idToStringWithoutBraces());
|
|
||||||
Logger::logToDb(Logger::makeErrorMsg("Db", message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto &id : idsToDelete)
|
|
||||||
tokensToUpdateFromDBToColnod.remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataManager::filterTokensColnod()
|
|
||||||
{
|
|
||||||
QList<QString> idsToDelete;
|
|
||||||
for (const auto &token : tokensToUpdateFromColnodToDB)
|
|
||||||
{
|
|
||||||
QString id = token->idToStringWithoutBraces();
|
|
||||||
auto record = db->getToken(id);
|
|
||||||
if (!record.isValid())
|
|
||||||
continue;
|
|
||||||
auto tokenDB = std::make_shared<Token>();
|
|
||||||
tokenDB->fromSQLRecord(record);
|
|
||||||
if (*tokenDB == *token || *tokenDB > *token){
|
|
||||||
idsToDelete.append(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto &id : idsToDelete)
|
|
||||||
tokensToUpdateFromColnodToDB.remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync last update from colnod to DB
|
|
||||||
void DataManager::setLastUpdate()
|
|
||||||
{
|
|
||||||
if (!tokensToUpdateFromColnodToDB.isEmpty())
|
|
||||||
for (const auto &token : tokensToUpdateFromColnodToDB)
|
|
||||||
db->setLastUpdate(token->idToStringWithoutBraces(), token->getLastUpdate());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataManager::setLastUpdate(const QString &id)
|
|
||||||
{
|
|
||||||
db->setLastUpdate(id, nextLastSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "token.h"
|
|
||||||
#include "subject.h"
|
|
||||||
#include "requestmanager.h"
|
|
||||||
#include "databasemanager.h"
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <qjsonarray.h>
|
|
||||||
#include <logger.h>
|
|
||||||
|
|
||||||
|
|
||||||
class DataManager : public QObject
|
|
||||||
{
|
|
||||||
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit DataManager(const bool& dryMode = false);
|
|
||||||
|
|
||||||
void fillFullTokensColnod(const QString& jsonResponse);
|
|
||||||
|
|
||||||
void fillTokensToUpdateColnod(const QString& jsonResponse);
|
|
||||||
void fillSubjectsToUpdateColnod(const QString& jsonResponse);
|
|
||||||
void fillSubjectsToDeleteColnod(const QString& jsonResponse);
|
|
||||||
void fillTokensToDeleteColnod(const QString& jsonResponse);
|
|
||||||
|
|
||||||
void fillTokensToChangeDB();
|
|
||||||
void fillSubjectsToUpdateDB();
|
|
||||||
void fillSubjectsToDeleteDB();
|
|
||||||
|
|
||||||
void filterTokensDB();
|
|
||||||
void filterTokensColnod();
|
|
||||||
|
|
||||||
void pushSubjectsToDB();
|
|
||||||
void pushTokensToDB();
|
|
||||||
void deleteSubjectsFromDB();
|
|
||||||
void setDeletedToTokensFromDB();
|
|
||||||
|
|
||||||
void updateLastSync();
|
|
||||||
|
|
||||||
void dropTables();
|
|
||||||
|
|
||||||
void createTables();
|
|
||||||
|
|
||||||
quint64 getLastModifiedOfToken(const QString &id);
|
|
||||||
|
|
||||||
QHash<QString, std::shared_ptr<Token>> getTokensToUpdateFromColnod() {return tokensToUpdateFromColnodToDB;}
|
|
||||||
QList<QString> getTokensToDeleteColnod() {return tokensFromColnodToDeleteFromDB;}
|
|
||||||
QHash<QString, std::shared_ptr<Token>> getTokensToUpdateFromDB() {return tokensToUpdateFromDBToColnod;}
|
|
||||||
QList<QString> getTokensToDeleteDB() {return tokensFromDBToDeleteFromColnod;}
|
|
||||||
|
|
||||||
const quint64 &getLastSync() {return lastSync;}
|
|
||||||
const quint64 &getCurrentSync() {return nextLastSync;}
|
|
||||||
|
|
||||||
bool isDBOpened() {return db->isOpen();}
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void clear();
|
|
||||||
void setLastUpdate();
|
|
||||||
void setLastUpdate(const QString &id);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void error();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<DatabaseManager> db;
|
|
||||||
|
|
||||||
quint64 lastSync; // time of last run
|
|
||||||
quint64 nextLastSync; // time of begin of this run
|
|
||||||
|
|
||||||
QHash<QString, std::shared_ptr<Token>> allTokensFromColnodById;
|
|
||||||
QHash<QString, std::shared_ptr<Token>> allTokensFromColnodByName;
|
|
||||||
QHash<QString, std::shared_ptr<Subject>> subjectsToUpdateFromColnodToDB;
|
|
||||||
QHash<QString, std::shared_ptr<Token>> tokensToUpdateFromColnodToDB;
|
|
||||||
QHash<QString, std::shared_ptr<Subject>> subjectsDB; // in future
|
|
||||||
QHash<QString, std::shared_ptr<Token>> tokensToUpdateFromDBToColnod;
|
|
||||||
|
|
||||||
QList<QString> subjectsFromColnodToDeleteFromDB;
|
|
||||||
QList<QString> tokensFromColnodToDeleteFromDB;
|
|
||||||
QList<QString> subjectsToDeleteDB;
|
|
||||||
QList<QString> tokensFromDBToDeleteFromColnod;
|
|
||||||
|
|
||||||
bool dryMode = false;
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
#include <QJsonObject>
|
|
||||||
#include "entity.h"
|
|
||||||
|
|
||||||
const QUuid &Entity::getId() const {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setId(const QUuid &id) {
|
|
||||||
Entity::id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &Entity::getName() const {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setName(const QString &name) {
|
|
||||||
Entity::name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &Entity::getDescription() const {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setDescription(const QString &description) {
|
|
||||||
Entity::description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 Entity::getLastUpdate() const {
|
|
||||||
return lastUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setLastUpdate(quint64 lastUpdate) {
|
|
||||||
this->lastUpdate = lastUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 Entity::getLastModified() const {
|
|
||||||
return lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setLastModified(const quint64 &lastModified) {
|
|
||||||
this->lastModified = lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entity::getDeleted() const
|
|
||||||
{
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setDeleted(const bool &deleted)
|
|
||||||
{
|
|
||||||
this->deleted = deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Entity::idToStringWithoutBraces() const
|
|
||||||
{
|
|
||||||
const qint8 lengthOfUuid = 36;
|
|
||||||
return id.toString().mid(1, lengthOfUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::fromJSON(const QJsonObject &json) {
|
|
||||||
id = json["id"].toString();
|
|
||||||
name = json["name"].toString();
|
|
||||||
description = json["description"].toString();
|
|
||||||
lastUpdate = json["lastUpdate"].toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject Entity::toJSON() const {
|
|
||||||
QJsonObject json;
|
|
||||||
|
|
||||||
json["id"] = this->idToStringWithoutBraces();
|
|
||||||
json["name"] = name;
|
|
||||||
json["description"] = description;
|
|
||||||
json["lastUpdate"] = lastUpdate;
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
|
|
||||||
class Entity : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Entity() = default;
|
|
||||||
using QObject::QObject;
|
|
||||||
|
|
||||||
const QUuid &getId() const;
|
|
||||||
void setId(const QUuid &id);
|
|
||||||
|
|
||||||
const QString &getName() const;
|
|
||||||
void setName(const QString &name);
|
|
||||||
|
|
||||||
const QString &getDescription() const;
|
|
||||||
void setDescription(const QString &description);
|
|
||||||
|
|
||||||
quint64 getLastUpdate() const;
|
|
||||||
void setLastUpdate(quint64 lastUpdate);
|
|
||||||
|
|
||||||
quint64 getLastModified() const;
|
|
||||||
void setLastModified(const quint64 &lastModified);
|
|
||||||
|
|
||||||
bool getDeleted() const;
|
|
||||||
void setDeleted(const bool &deleted);
|
|
||||||
|
|
||||||
QString idToStringWithoutBraces() const;
|
|
||||||
|
|
||||||
virtual void fromJSON(const QJsonObject &json);
|
|
||||||
virtual QJsonObject toJSON() const;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
QUuid id;
|
|
||||||
QString name;
|
|
||||||
QString description;
|
|
||||||
qint64 lastUpdate{};
|
|
||||||
quint64 lastModified{};
|
|
||||||
bool deleted{};
|
|
||||||
};
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
#include "logger.h"
|
|
||||||
#include "databasemanager.h"
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
std::shared_ptr<DatabaseManager> Logger::dbMan = nullptr;
|
|
||||||
Logger *Logger::logger = nullptr;
|
|
||||||
|
|
||||||
Logger::Logger()
|
|
||||||
{
|
|
||||||
dbMan = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger *Logger::getLogger()
|
|
||||||
{
|
|
||||||
if (!logger)
|
|
||||||
logger = new Logger;
|
|
||||||
return logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::log(const QString& msg)
|
|
||||||
{
|
|
||||||
qDebug() << msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::logToDb(const QString &msg)
|
|
||||||
{
|
|
||||||
if (dbMan)
|
|
||||||
Logger::dbMan->log(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::setDBMan(std::shared_ptr<DatabaseManager> dbMan)
|
|
||||||
{
|
|
||||||
Logger::dbMan = std::move(dbMan);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Logger::makeTokenMsg(const QString &tokenId, const QString &action, const QString &source)
|
|
||||||
{
|
|
||||||
return QString("Token: %1 of subject: %2 %3. Source: %4").arg(tokenId,dbMan->getSubjectNameByTokenId(tokenId), action, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Logger::makeSubjectMsg(const QString &subjectId, const QString &action, const QString &source)
|
|
||||||
{
|
|
||||||
return QString("Subject: %1 %2. Source: %3").arg(dbMan->getSubjectNameById(subjectId), action, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Logger::makeErrorMsg(const QString &source, const QString &errMsg)
|
|
||||||
{
|
|
||||||
return QString("Source: %1, Error: %2").arg(source, errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QSqlDatabase>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class DatabaseManager;
|
|
||||||
|
|
||||||
|
|
||||||
class Logger
|
|
||||||
{
|
|
||||||
|
|
||||||
static Logger *logger;
|
|
||||||
|
|
||||||
Logger();
|
|
||||||
|
|
||||||
static std::shared_ptr<DatabaseManager> dbMan;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static Logger *getLogger();
|
|
||||||
static void log(const QString& msg);
|
|
||||||
static void logToDb(const QString& msg);
|
|
||||||
static void setDBMan(std::shared_ptr<DatabaseManager> dbMan);
|
|
||||||
static QString makeTokenMsg(const QString& tokenId, const QString& action, const QString& source);
|
|
||||||
static QString makeSubjectMsg(const QString& subjectId, const QString& action, const QString& source);
|
|
||||||
static QString makeErrorMsg(const QString& source, const QString& errMsg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,248 +0,0 @@
|
|||||||
#include "requestmanager.h"
|
|
||||||
|
|
||||||
RequestManager::RequestManager()
|
|
||||||
: manager(std::make_unique<QNetworkAccessManager>())
|
|
||||||
{
|
|
||||||
connect(manager.get(), &QNetworkAccessManager::sslErrors, this, &RequestManager::onSSLError);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Response> RequestManager::login()
|
|
||||||
{
|
|
||||||
Q_ASSERT(!loginResponse);
|
|
||||||
loginResponse = std::make_shared<Response>();
|
|
||||||
QNetworkRequest request;
|
|
||||||
QString hash = encryptedPassword(this->password);
|
|
||||||
authHeader = getLoginHeader(this->user, hash);
|
|
||||||
QJsonObject reqBody = {
|
|
||||||
{"password", hash},
|
|
||||||
{"username", this->user}
|
|
||||||
};
|
|
||||||
request.setUrl(this->host + "/AaaManager/authSession");
|
|
||||||
request.setRawHeader("Authorization", authHeader.toLocal8Bit());
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
|
|
||||||
QJsonDocument jsonDocument;
|
|
||||||
jsonDocument.setObject(reqBody);
|
|
||||||
QNetworkReply* reply = manager->post(request, jsonDocument.toJson());
|
|
||||||
loginResponse->setReply(reply);
|
|
||||||
connect(reply, &QNetworkReply::finished, [=]() {
|
|
||||||
if (reply->error())
|
|
||||||
{
|
|
||||||
Logger::logToDb(Logger::makeErrorMsg("Request Manager", reply->errorString()));
|
|
||||||
loginStatus = false;
|
|
||||||
reply->deleteLater();
|
|
||||||
emit error();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loginStatus = true;
|
|
||||||
emit successLogin();
|
|
||||||
reply->deleteLater();
|
|
||||||
});
|
|
||||||
connect(this, &RequestManager::successLogin, loginResponse.get(), &Response::ready);
|
|
||||||
connect(this, &RequestManager::successLogin, this, &RequestManager::prepareAuthHeader);
|
|
||||||
return loginResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply* RequestManager::postRequest(const QString& endpoint, const QByteArray& json)
|
|
||||||
{
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setUrl(host + endpoint);
|
|
||||||
request.setRawHeader("Authorization", authHeader.toLocal8Bit());
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
|
|
||||||
QNetworkReply* reply = manager->post(request, json);
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=]() {
|
|
||||||
if (reply->error())
|
|
||||||
{
|
|
||||||
Logger::logToDb(Logger::makeErrorMsg("Request Manager", reply->errorString()));
|
|
||||||
loginStatus = false;
|
|
||||||
reply->deleteLater();
|
|
||||||
emit error();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emit responseReady();
|
|
||||||
reply->deleteLater();
|
|
||||||
});
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will ensure login is called only once
|
|
||||||
std::shared_ptr<Response> RequestManager::getLoginResponse() {
|
|
||||||
if (!loginResponse){
|
|
||||||
loginResponse = login();
|
|
||||||
}
|
|
||||||
return loginResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::postRequestReturnLogic(const QString& endpoint, const QByteArray& data)
|
|
||||||
{
|
|
||||||
auto loginResponse = getLoginResponse();
|
|
||||||
Q_ASSERT(loginResponse);
|
|
||||||
auto response = new Response;
|
|
||||||
|
|
||||||
if (!loginStatus){
|
|
||||||
connect(this, &RequestManager::successLogin, [=](){
|
|
||||||
response->setReply(postRequest(endpoint, data));
|
|
||||||
connect(response->getReply(), &QNetworkReply::finished, response, &Response::ready);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response->setReply(postRequest(endpoint, data));
|
|
||||||
connect(response->getReply(), &QNetworkReply::finished, response, &Response::ready);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get value of member in json
|
|
||||||
QString RequestManager::getFromJson(const QString& json, const QString &member)
|
|
||||||
{
|
|
||||||
QJsonParseError err{};
|
|
||||||
QJsonDocument document = QJsonDocument::fromJson(json.toUtf8(),&err);
|
|
||||||
if (err.error != 0)
|
|
||||||
{
|
|
||||||
qDebug() << err.errorString();
|
|
||||||
}
|
|
||||||
return document.object().value(member).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString RequestManager::encryptedPassword(const QString& password)
|
|
||||||
{
|
|
||||||
return QCryptographicHash::hash(password.toLocal8Bit(), QCryptographicHash::Sha512).toHex().toUpper();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString RequestManager::getLoginHeader(const QString& username, const QString& hash)
|
|
||||||
{
|
|
||||||
QString authValue = username + ":" + hash;
|
|
||||||
return "Basic " + authValue.toLocal8Bit().toBase64();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestManager::prepareAuthHeader()
|
|
||||||
{
|
|
||||||
jsonResponse = loginResponse->getJson();
|
|
||||||
QString token = getFromJson(jsonResponse, "id");
|
|
||||||
QString authValue = token + ":";
|
|
||||||
authHeader = "Basic " + authValue.toLocal8Bit().toBase64();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestManager::onSSLError(QNetworkReply *reply, const QList<QSslError> &errors)
|
|
||||||
{
|
|
||||||
//qDebug() << "Network SSL errors";
|
|
||||||
reply->ignoreSslErrors(errors);
|
|
||||||
// for (const auto &error : errors)
|
|
||||||
// {
|
|
||||||
// if (error.error() == QSslError::SelfSignedCertificate)
|
|
||||||
// reply->ignoreSslErrors(errors);
|
|
||||||
//qDebug() << error.errorString();
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray RequestManager::makeTimestampJson(uint64_t time)
|
|
||||||
{
|
|
||||||
QJsonObject reqBody = {{"timestamp",QJsonValue(static_cast<qint64>(time)) }};
|
|
||||||
QJsonDocument doc;
|
|
||||||
doc.setObject(reqBody);
|
|
||||||
return doc.toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray RequestManager::makeTokensJson(const QHash<QString, std::shared_ptr<Token>> &tokens)
|
|
||||||
{
|
|
||||||
QJsonObject items;
|
|
||||||
QJsonArray jsonTokens;
|
|
||||||
for (const auto& token : tokens)
|
|
||||||
{
|
|
||||||
jsonTokens.push_back(token->toJSON());
|
|
||||||
}
|
|
||||||
items.insert("items", jsonTokens);
|
|
||||||
QJsonDocument doc;
|
|
||||||
doc.setObject(items);
|
|
||||||
return doc.toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray RequestManager::makeIdsJson(const QList<QString> &ids)
|
|
||||||
{
|
|
||||||
QJsonArray jsonIds;
|
|
||||||
for (const auto& id : ids)
|
|
||||||
{
|
|
||||||
jsonIds.push_back(id);
|
|
||||||
}
|
|
||||||
QJsonObject items;
|
|
||||||
items.insert("items", jsonIds);
|
|
||||||
QJsonDocument doc;
|
|
||||||
doc.setObject(items);
|
|
||||||
return doc.toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getSubjects()
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/SubjectManager/getSubjects");
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getTokens()
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/TokenManager/getTokens");
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getTokenSubject()
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/OpenManager/getTokenSubject");
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getSubjectsUpdated(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/SubjectManager/getSubjectsUpdated", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getSubjectsDeleted(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/SubjectManager/getSubjectsDeleted", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getTokensUpdated(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/TokenManager/getTokensUpdated", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getTokensDeleted(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/TokenManager/getTokensDeleted", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getAccessGroups()
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/AccessManager/getAccessGroups");
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getAccessGroupsUpdated(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/AccessManager/getAccessGroupsUpdated", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getAccessGroupsDeleted(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/AccessManager/getAccessGroupsDeleted", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getAccessGroupMembership()
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/AccessManager/getAccessGroupMembership");
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getAccessGroupMembershipUpdated(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/AccessManager/getAccessGroupMembershipUpdated", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::getAccessGroupMembershipDeleted(uint64_t time)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/AccessManager/getAccessGroupMembershipDeleted", makeTimestampJson(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::setTokens(const QHash<QString, std::shared_ptr<Token>> &tokens)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/TokenManager/setTokens", makeTokensJson(tokens));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response* RequestManager::deleteTokens(const QList<QString> &ids)
|
|
||||||
{
|
|
||||||
return postRequestReturnLogic("/TokenManager/deleteTokens", makeIdsJson(ids));
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <qcoreapplication.h>
|
|
||||||
#include <qnetworkreply.h>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonParseError>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QString>
|
|
||||||
#include <QMultiMap>
|
|
||||||
#include <QPointer>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QSslError>
|
|
||||||
#include <QPair>
|
|
||||||
|
|
||||||
|
|
||||||
#include "token.h"
|
|
||||||
#include "response.h"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
class RequestManager : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
friend class UnitTest;
|
|
||||||
friend class ComplexTest;
|
|
||||||
|
|
||||||
RequestManager();
|
|
||||||
|
|
||||||
// this should be in ColnodAPI class BEGIN
|
|
||||||
Response* getSubjects();
|
|
||||||
Response* getTokens();
|
|
||||||
Response* getTokenSubject();
|
|
||||||
|
|
||||||
Response* getSubjectsUpdated(uint64_t time);
|
|
||||||
Response* getSubjectsDeleted(uint64_t time);
|
|
||||||
|
|
||||||
Response* getTokensUpdated(uint64_t time);
|
|
||||||
Response* getTokensDeleted(uint64_t time);
|
|
||||||
|
|
||||||
Response* getAccessGroups();
|
|
||||||
Response* getAccessGroupsUpdated(uint64_t time);
|
|
||||||
Response* getAccessGroupsDeleted(uint64_t time);
|
|
||||||
Response* getAccessGroupMembership();
|
|
||||||
Response* getAccessGroupMembershipUpdated(uint64_t time);
|
|
||||||
Response* getAccessGroupMembershipDeleted(uint64_t time);
|
|
||||||
|
|
||||||
Response* setTokens(const QHash<QString, std::shared_ptr<Token>> &tokens);
|
|
||||||
Response* deleteTokens(const QList<QString> &ids);
|
|
||||||
// END
|
|
||||||
|
|
||||||
private:
|
|
||||||
// should be public
|
|
||||||
QNetworkReply* postRequest(const QString& endpoint, const QByteArray& json = QByteArray());
|
|
||||||
Response* postRequestReturnLogic(const QString& endpoint, const QByteArray& data = QByteArray());
|
|
||||||
|
|
||||||
std::shared_ptr<Response> login();
|
|
||||||
std::shared_ptr<Response> getLoginResponse();
|
|
||||||
QString getFromJson(const QString& json, const QString &member);
|
|
||||||
QString encryptedPassword(const QString& password);
|
|
||||||
QString getLoginHeader(const QString& username, const QString& hash);
|
|
||||||
|
|
||||||
// in colnod api as well
|
|
||||||
QByteArray makeTimestampJson(uint64_t time);
|
|
||||||
QByteArray makeTokensJson(const QHash<QString, std::shared_ptr<Token>> &tokens); // maybe better to be in Token class
|
|
||||||
QByteArray makeIdsJson(const QList<QString> &ids); // Entity class
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onSSLError(QNetworkReply *reply, const QList<QSslError> &errors);
|
|
||||||
//void onReplyFinished(QNetworkReply *reply);
|
|
||||||
void prepareAuthHeader();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void successLogin();
|
|
||||||
void responseReady();
|
|
||||||
void error();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<QNetworkAccessManager> manager;
|
|
||||||
std::shared_ptr<Response> loginResponse;
|
|
||||||
QJsonDocument config;
|
|
||||||
QString authHeader;
|
|
||||||
const QString host = "https://192.168.1.3:8443";
|
|
||||||
//QString host = "https://localhost:8080";
|
|
||||||
|
|
||||||
const QString password = "spaceti1";
|
|
||||||
const QString user = "spaceti";
|
|
||||||
QString jsonResponse;
|
|
||||||
bool loginStatus = false;
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
#include "response.h"
|
|
||||||
|
|
||||||
Response::Response()
|
|
||||||
{
|
|
||||||
connect(this, &Response::ready, this, &Response::setJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Response::getStatusCode()
|
|
||||||
{
|
|
||||||
return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <qnetworkreply.h>
|
|
||||||
|
|
||||||
class Response : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
|
|
||||||
Response();
|
|
||||||
|
|
||||||
int getStatusCode();
|
|
||||||
QString getJson() {return json;}
|
|
||||||
void setReply(QNetworkReply* rpl) {reply = rpl;}
|
|
||||||
QNetworkReply* getReply() {return reply;}
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
void ready();
|
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void setJson() {json = reply->readAll();}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString json;
|
|
||||||
QNetworkReply *reply = nullptr;
|
|
||||||
};
|
|
||||||
@ -1,90 +1,36 @@
|
|||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "subject.h"
|
#include "subject.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
|
||||||
|
|
||||||
Subject::Subject(const QString &name)
|
Subject::Subject(const QString &name)
|
||||||
{
|
{
|
||||||
setName(name);
|
setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Subject::getEmail() const {
|
bool Subject::fromJson(const QByteArray &data)
|
||||||
return attributes["email"].toString();
|
{
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
QJsonObject attr = doc.object()["attributes"].toObject()["items"].toObject();
|
||||||
|
auto id = doc.object()["id"].toString();
|
||||||
|
|
||||||
|
for (const auto &key : attr.keys()) {
|
||||||
|
auto newAtr = std::make_shared<SubjectAttribute>();
|
||||||
|
newAtr->setId(id + " " + key);
|
||||||
|
newAtr->setSubjectId(id);
|
||||||
|
newAtr->setAttributeKey(key);
|
||||||
|
newAtr->setAttributeValue(attr.value(key).toString());
|
||||||
|
attributes.append(newAtr);
|
||||||
|
}
|
||||||
|
QVariantMap variant = qvariant_cast<QVariantMap>(doc.toVariant());
|
||||||
|
auto lastUpdate = variant["lastUpdate"].toLongLong();
|
||||||
|
variant["lastUpdate"] = QDateTime::fromMSecsSinceEpoch(lastUpdate);
|
||||||
|
variant.insert("lastModified", QDateTime::fromMSecsSinceEpoch(lastUpdate));
|
||||||
|
|
||||||
|
// Populate this object data
|
||||||
|
bool res = fromVariantMap(variant);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Subject::fromJSON(const QJsonObject &json) {
|
QList<std::shared_ptr<SubjectAttribute> > Subject::getAttributes()
|
||||||
Entity::fromJSON(json);
|
{
|
||||||
|
|
||||||
attributes = json["attributes"].toObject()["items"].toObject();
|
|
||||||
for (const auto & attributeType : attributeTypes)
|
|
||||||
{
|
|
||||||
if (!attributes[attributeType].toString().isEmpty()){
|
|
||||||
atr.append(qMakePair(attributeType, attributes[attributeType].toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto &tokenFlagsArray = json["tokenFlags"].toArray();
|
|
||||||
for(const auto &flag : tokenFlagsArray){
|
|
||||||
tokenFlags.insert(flag.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
pin = json["pin"].toString();
|
|
||||||
|
|
||||||
const auto &pinFlagsArray = json["pinFlags"].toArray();
|
|
||||||
for(const auto &flag : pinFlagsArray){
|
|
||||||
pinFlags.insert(flag.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject Subject::toJSON() const {
|
|
||||||
QJsonObject json = Entity::toJSON();
|
|
||||||
|
|
||||||
QJsonObject items({{"items", attributes}});
|
|
||||||
json["atributes"] = items;
|
|
||||||
json["tokenFlags"] = QJsonArray::fromStringList(tokenFlags.toList());
|
|
||||||
json["pin"] = pin;
|
|
||||||
json["pinFlags"] = QJsonArray::fromStringList(pinFlags.toList());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QJsonObject &Subject::getAttributes() const {
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Subject::setAttributes(const QJsonObject &attributes) {
|
|
||||||
Subject::attributes = attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QSet<QString> &Subject::getTokenFlags() const {
|
|
||||||
return tokenFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Subject::setTokenFlags(const QSet<QString> &tokenFlags) {
|
|
||||||
Subject::tokenFlags = tokenFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &Subject::getPin() const {
|
|
||||||
return pin;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Subject::setPin(const QString &pin) {
|
|
||||||
Subject::pin = pin;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QSet<QString> &Subject::getPinFlags() const {
|
|
||||||
return pinFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Subject::setPinFlags(const QSet<QString> &pinFlags) {
|
|
||||||
Subject::pinFlags = pinFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<QPair<QString, QString>> &Subject::getAtr() const {
|
|
||||||
return atr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Subject::setAtr(const QList<QPair<QString, QString>> &atr) {
|
|
||||||
this->atr = atr;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,41 +1,35 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QSet>
|
#include <QDateTime>
|
||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
#include <qjsondocument.h>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "entity.h"
|
class SubjectAttribute : public serversdk::DatabaseTable {
|
||||||
#include <QDebug>
|
Q_OBJECT
|
||||||
#include <QPointer>
|
TABLE_PROPERTY(QString, id, setId, "")
|
||||||
#include <QPair>
|
TABLE_PROPERTY(QString, attributeKey, setAttributeKey, "")
|
||||||
|
TABLE_PROPERTY(QString, attributeValue, setAttributeValue, "")
|
||||||
|
TABLE_PROPERTY(QString, subjectId, setSubjectId, "")
|
||||||
|
};
|
||||||
|
|
||||||
class Subject : public Entity {
|
class Subject : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, id, setId, QString())
|
||||||
|
TABLE_PROPERTY(QString, description, setDescription, "")
|
||||||
|
TABLE_PROPERTY(QDateTime, lastUpdate, setLastUpdate, QDateTime())
|
||||||
|
TABLE_PROPERTY(QString, name, setName, "")
|
||||||
|
TABLE_PROPERTY(QString, pin, setP, "")
|
||||||
|
TABLE_PROPERTY(QDateTime, lastModified, setLastModified, QDateTime())
|
||||||
|
TABLE_PROPERTY(bool, deleted, setDeleted, false)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Entity::Entity;
|
|
||||||
Subject() = default;
|
Subject() = default;
|
||||||
explicit Subject(const QString& name);
|
explicit Subject(const QString& name);
|
||||||
|
|
||||||
QString getEmail() const;
|
bool fromJson(const QByteArray &data) override;
|
||||||
|
QList<std::shared_ptr <SubjectAttribute>> getAttributes();
|
||||||
|
|
||||||
const QJsonObject &getAttributes() const;
|
|
||||||
void setAttributes(const QJsonObject &attributes);
|
|
||||||
const QSet<QString> &getTokenFlags() const;
|
|
||||||
void setTokenFlags(const QSet<QString> &tokenFlags);
|
|
||||||
const QString &getPin() const;
|
|
||||||
void setPin(const QString &pin);
|
|
||||||
const QSet<QString> &getPinFlags() const;
|
|
||||||
void setPinFlags(const QSet<QString> &pinFlags);
|
|
||||||
|
|
||||||
const QList<QPair<QString, QString>> &getAtr() const;
|
|
||||||
void setAtr(const QList<QPair<QString, QString>> &atr);
|
|
||||||
|
|
||||||
void fromJSON(const QJsonObject &json) override;
|
|
||||||
QJsonObject toJSON() const override;
|
|
||||||
private:
|
private:
|
||||||
QJsonObject attributes;
|
QList<std::shared_ptr <SubjectAttribute>> attributes;
|
||||||
QSet<QString> tokenFlags;
|
|
||||||
QString pin;
|
|
||||||
QSet<QString> pinFlags;
|
|
||||||
QList<QPair<QString, QString>> atr;
|
|
||||||
const QList<QString> attributeTypes = {"Jmeno", "Prijmeni", "ProfilParking", "Spolecnost", "Profil", "spz"};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,213 +1,226 @@
|
|||||||
#include "syncmanager.h"
|
#include "syncmanager.h"
|
||||||
|
|
||||||
SyncManager::SyncManager(bool dryMode) :
|
|
||||||
reqMan(std::make_unique<RequestManager>()),
|
Q_LOGGING_CATEGORY(syncmanager, "sync.manager");
|
||||||
dataMan( std::make_unique<DataManager>(dryMode)),
|
|
||||||
dryMode(dryMode)
|
|
||||||
|
SyncManager::SyncManager(bool dryMode)
|
||||||
|
:mDryMode(dryMode)
|
||||||
{
|
{
|
||||||
connect(reqMan.get(), &RequestManager::error, this, &SyncManager::error, Qt::QueuedConnection);
|
|
||||||
connect(dataMan.get(), &DataManager::error, this, &SyncManager::error, Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SyncManager::loadDataFromColnod()
|
|
||||||
{
|
|
||||||
loadTokensFromColnod();
|
|
||||||
loadUpdatedDataFromColnod();
|
|
||||||
loadDeletedDataFromColnod();
|
|
||||||
}
|
|
||||||
|
|
||||||
// download all tokens form colnod for comparing with tokens to update from DB
|
|
||||||
void SyncManager::loadTokensFromColnod()
|
|
||||||
{
|
|
||||||
Response* resp = reqMan->getTokens();
|
|
||||||
connect(resp, &Response::ready, [=](){
|
|
||||||
dataMan->fillFullTokensColnod(resp->getJson());
|
|
||||||
areChangedDataReady();
|
|
||||||
resp->deleteLater();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill tokens/subjects to update in data manager
|
|
||||||
void SyncManager::loadUpdatedDataFromColnod()
|
|
||||||
{
|
|
||||||
uint64_t lastUpdate = dataMan->getLastSync();
|
|
||||||
Response* resp0 = reqMan->getSubjectsUpdated(lastUpdate);
|
|
||||||
connect(resp0, &Response::ready, [=](){
|
|
||||||
dataMan->fillSubjectsToUpdateColnod(resp0->getJson());
|
|
||||||
areChangedDataReady();
|
|
||||||
resp0->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
Response* resp1 = reqMan->getTokensUpdated(lastUpdate);
|
|
||||||
connect(resp1, &Response::ready, [=](){
|
|
||||||
dataMan->fillTokensToUpdateColnod(resp1->getJson());
|
|
||||||
areChangedDataReady();
|
|
||||||
resp1->deleteLater();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill tokens/subjects to delete in data manager
|
|
||||||
void SyncManager::loadDeletedDataFromColnod()
|
|
||||||
{
|
|
||||||
uint64_t lastUpdate = dataMan->getLastSync();
|
|
||||||
Response* resp2 = reqMan->getSubjectsDeleted(lastUpdate);
|
|
||||||
connect(resp2, &Response::ready, [=](){
|
|
||||||
dataMan->fillSubjectsToDeleteColnod(resp2->getJson());
|
|
||||||
areChangedDataReady();
|
|
||||||
resp2->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
Response* resp3 = reqMan->getTokensDeleted(lastUpdate);
|
|
||||||
connect(resp3, &Response::ready, [=](){
|
|
||||||
dataMan->fillTokensToDeleteColnod(resp3->getJson());
|
|
||||||
areChangedDataReady();
|
|
||||||
resp3->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill structures in data manager (tokens to updata/to delte)
|
bool SyncManager::databaseInitialization()
|
||||||
void SyncManager::loadDataFromDB()
|
|
||||||
{
|
{
|
||||||
dataMan->fillTokensToChangeDB();
|
database = std::make_shared<DatabaseAPI>(&dbMan, mDryMode);
|
||||||
}
|
|
||||||
|
|
||||||
void SyncManager::syncDataFromColnodToDB()
|
// Open database
|
||||||
{
|
if (!database->open()) {
|
||||||
syncUpdatedDataFromColnodToDB();
|
qCritical(syncmanager) << "Not possible to open database:" << dbMan.database().hostName();
|
||||||
if (dataMan->getLastSync() != 0)
|
return false;
|
||||||
syncDeletedDataFromColnodToDB();
|
|
||||||
}
|
|
||||||
|
|
||||||
// push data from colnod to DB
|
|
||||||
void SyncManager::syncUpdatedDataFromColnodToDB()
|
|
||||||
{
|
|
||||||
dataMan->pushSubjectsToDB();
|
|
||||||
dataMan->pushTokensToDB();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set deleted tokens from Colnod, delete subject
|
|
||||||
void SyncManager::syncDeletedDataFromColnodToDB()
|
|
||||||
{
|
|
||||||
dataMan->deleteSubjectsFromDB();
|
|
||||||
dataMan->setDeletedToTokensFromDB();
|
|
||||||
}
|
|
||||||
|
|
||||||
// push changes form DB to Colnod
|
|
||||||
void SyncManager::syncDataFromDBToColnod()
|
|
||||||
{
|
|
||||||
|
|
||||||
connect(this, &SyncManager::dataUpdatedToColnod, dataMan.get(), &DataManager::clear);
|
|
||||||
//connect(this, &SyncManager::dataUpdatedToColnod, dataMan.get(), &DataManager::setLastSync);
|
|
||||||
if (!dryMode)
|
|
||||||
connect(this, &SyncManager::dataUpdatedToColnod, [=](){
|
|
||||||
auto resp = reqMan->getTokensUpdated(dataMan->getCurrentSync());
|
|
||||||
connect(resp, &Response::ready, [=](){
|
|
||||||
dataMan->fillTokensToUpdateColnod(resp->getJson());
|
|
||||||
dataMan->setLastUpdate();
|
|
||||||
dataMan->clear();
|
|
||||||
dataMan->updateLastSync();
|
|
||||||
emit syncFinished();
|
|
||||||
resp->deleteLater();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
else
|
|
||||||
connect(this, &SyncManager::dataUpdatedToColnod, [=](){
|
|
||||||
dataMan->updateLastSync();
|
|
||||||
emit syncFinished();
|
|
||||||
});
|
|
||||||
if (!dataMan->getTokensToUpdateFromDB().isEmpty()){
|
|
||||||
syncUpdatedDataFromDBToColnod();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
updateFinished = true;
|
// Get last synchronizaiton date
|
||||||
if (!dataMan->getTokensToDeleteDB().isEmpty()){
|
qint64 lastsync_in_ms = database->getLastSync();
|
||||||
syncDeletedDataFromDBToColnod();
|
if (lastsync_in_ms == -1) {
|
||||||
}
|
qCritical(syncmanager) << "Not possible to get last synchronization date";
|
||||||
else{
|
return false;
|
||||||
deleteFinished = true;
|
|
||||||
areDataUpdatedToColnod();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// here -> nothing can fail
|
||||||
|
mlastSync.setTimestamp(lastsync_in_ms);
|
||||||
|
colnod = std::make_shared<ColnodAPI>(&reqMan, mDryMode);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add or update tokens to Colnod
|
|
||||||
void SyncManager::syncUpdatedDataFromDBToColnod()
|
|
||||||
{
|
|
||||||
dataMan->filterTokensDB();
|
|
||||||
auto tokensToUpdate = dataMan->getTokensToUpdateFromDB();
|
|
||||||
if (dryMode){
|
|
||||||
for (const auto &token : tokensToUpdate)
|
|
||||||
qInfo() << "Updating token:" << token->idToStringWithoutBraces() << "to Colnod";
|
|
||||||
updateFinished = true;
|
|
||||||
areDataUpdatedToColnod();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Response* resp0 = reqMan->setTokens(tokensToUpdate);
|
|
||||||
connect(resp0, &Response::ready, [=](){
|
|
||||||
qInfo() << "Colnod:" << tokensToUpdate.size() << "tokens updated";
|
|
||||||
for(const auto &token : tokensToUpdate)
|
|
||||||
Logger::logToDb(Logger::makeTokenMsg(token->idToStringWithoutBraces(), "updated", "Colnod"));
|
|
||||||
updateFinished = true;
|
|
||||||
areDataUpdatedToColnod();
|
|
||||||
resp0->deleteLater();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete tokens in Colnod
|
bool SyncManager::startSynchronization()
|
||||||
void SyncManager::syncDeletedDataFromDBToColnod()
|
|
||||||
{
|
{
|
||||||
auto tokensToDelete = dataMan->getTokensToDeleteDB();
|
// Need current time from db server
|
||||||
if (dryMode){
|
qint64 syncBegin = database->getNow();
|
||||||
for (const auto &token : tokensToDelete)
|
if (syncBegin == -1) {
|
||||||
qInfo() << "Deleting token:" << token << "from Colnod";
|
qCritical(syncmanager) << "Not possible to get last sync date";
|
||||||
deleteFinished = true;
|
return false;
|
||||||
areDataUpdatedToColnod();
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Response* resp1 = reqMan->deleteTokens(tokensToDelete);
|
qInfo(syncmanager) << tab << "- begin sync ( previous sync" << mlastSync.toDateString() << ")";
|
||||||
connect(resp1, &Response::ready, [=](){
|
|
||||||
qInfo() << "Colnod:" << tokensToDelete.size() << "tokens deleted";
|
// GETTING DATA FROM DATABASE
|
||||||
for (const auto &id : tokensToDelete){
|
auto databaseData = database->downloadData();
|
||||||
dataMan->setLastUpdate(id);
|
if (!databaseData) {
|
||||||
Logger::logToDb(Logger::makeTokenMsg(id, "deleted", "Colnod"));
|
qCritical(syncmanager) << "not possible to get data from database";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// NOW I HAVE DATA FROM DATABASE
|
||||||
|
|
||||||
|
colnod->downloadData(mlastSync.timestamp());
|
||||||
|
connect(colnod.get(), &ColnodAPI::downloadDataFinished, [=](const std::shared_ptr<Data> &colnodData) {
|
||||||
|
|
||||||
|
// NOW I HAVE DATA FROM COLNOD
|
||||||
|
if (!colnodData) {
|
||||||
|
qCritical(syncmanager) << "Not possible to get correct data from colnod";
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PreparedData> preparedData = prepareData(databaseData, colnodData);
|
||||||
|
if (!preparedData) {
|
||||||
|
qCritical(syncmanager) << "Not possible to compose data";
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database->processData(preparedData->colnodData)) {
|
||||||
|
qCritical(syncmanager) << "Not possible to upload data to database";
|
||||||
|
}
|
||||||
|
|
||||||
|
// data is processed to other sides
|
||||||
|
connect(colnod.get(), &ColnodAPI::dataProcessFinished, [&,syncBegin](bool success){
|
||||||
|
if (!success) {
|
||||||
|
qCritical(syncmanager) << "Not possible to upload data to colnod";
|
||||||
}
|
}
|
||||||
deleteFinished = true;
|
|
||||||
areDataUpdatedToColnod();
|
// REUPDATE tokens pushed from database to colnod (cause of lastUpdate time)
|
||||||
resp1->deleteLater();
|
Timestamp time;
|
||||||
|
time.setTimestamp(syncBegin);
|
||||||
|
|
||||||
|
// download data changes in this sync
|
||||||
|
auto tokensRequest = colnod->getTokensUpdated(time);
|
||||||
|
|
||||||
|
// updated data are ready
|
||||||
|
connect(tokensRequest.get(), &serversdk::Request::finished, [=](){
|
||||||
|
|
||||||
|
auto uploadedData = std::make_shared<Data>();
|
||||||
|
uploadedData->loadTokensFromJson(tokensRequest->responseData());
|
||||||
|
|
||||||
|
if (!uploadedData) {
|
||||||
|
qCritical(syncmanager) << "Not possible to get correct data from colnod";
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!database->uploadTokens(uploadedData->updatedTokens, {"lastModified"})) {
|
||||||
|
qCritical(syncmanager) << "Not possible to upload data to database";
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
database->reupdateDeleted(databaseData->deletedTokens);
|
||||||
|
|
||||||
|
if (!database->isValid() || !colnod->isValid()) {
|
||||||
|
qWarning(syncmanager) << "Unsuccessful FINISH, check warnings";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// set new time of last sync to this time of this run
|
||||||
|
if (!database->setLastSync(syncBegin)) {
|
||||||
|
qWarning(syncmanager) << "Unsuccessful FINISH, check warnings";
|
||||||
|
}
|
||||||
|
qInfo(syncmanager) << "Successful FINISH";
|
||||||
|
}
|
||||||
|
QCoreApplication::exit();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
colnod->processData(preparedData->databaseData);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// goes through downloaded data and check if consist conflicts (both sides edit), already sync/deleted tokens or new tokens
|
||||||
|
std::unique_ptr<PreparedData> SyncManager::prepareData(const std::shared_ptr<Data> &databaseData, const std::shared_ptr<Data> &colnodData)
|
||||||
|
{
|
||||||
|
auto preparedColnodData = std::make_shared<Data>();
|
||||||
|
auto preparedDatabaseData = std::make_shared<Data>();
|
||||||
|
|
||||||
|
QList<QString> conflicts;
|
||||||
|
QList<QString> alreadySynced;
|
||||||
|
|
||||||
|
for (const auto &databaseToken : databaseData->updatedTokens) {
|
||||||
|
if (databaseToken->name().isEmpty()) {
|
||||||
|
initNewToken(databaseToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restriction cauesed by colnod rest api
|
||||||
|
if (databaseToken->description() != "mobile") {
|
||||||
|
qCritical(syncmanager) << "Token:" << databaseToken->id() << "doesn't have 'mobile' in description, can't be ";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no conflict
|
||||||
|
if (!colnodData->updatedTokens.contains(databaseToken->id())) {
|
||||||
|
preparedDatabaseData->updatedTokens.insert(databaseToken->id(), databaseToken);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// database change is newer
|
||||||
|
if (databaseToken > colnodData->updatedTokens.value(databaseToken->id())) {
|
||||||
|
preparedDatabaseData->updatedTokens.insert(databaseToken->id(), databaseToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// For full sync
|
preparedDatabaseData->deletedTokens = databaseData->deletedTokens;
|
||||||
void SyncManager::dropTables()
|
|
||||||
{
|
|
||||||
dataMan->dropTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SyncManager::createTables()
|
qInfo(syncmanager) << tab << "- prep db:" << tab << "tokens [" << preparedDatabaseData->updatedTokens.size() << ":" << preparedDatabaseData->deletedTokens.size() <<
|
||||||
{
|
"] [ added/updated : deleted ]";
|
||||||
dataMan->createTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SyncManager::isValid()
|
// add all data which should be pushed to database
|
||||||
{
|
for (const auto &colnodToken : colnodData->updatedTokens) {
|
||||||
return dataMan->isDBOpened();
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit signal when all data is downloaded from Colnod
|
// not conflict so add to prepered tokens
|
||||||
void SyncManager::areChangedDataReady()
|
if (!databaseData->updatedTokens.contains(colnodToken->id())) {
|
||||||
{
|
auto token = database->getToken(colnodToken->id());
|
||||||
if (++dataCounter == 5)
|
if (!token) {
|
||||||
emit dataDownloaded();
|
qCritical(syncmanager) << "Not possible to access database.";
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
|
if ( token ) {
|
||||||
|
// alredy synced ( tokens are same )
|
||||||
|
if (token == colnodToken) {
|
||||||
|
alreadySynced.append(colnodToken->id());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preparedColnodData->updatedTokens.insert(colnodToken->id(), colnodToken);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// emit signal when writing to Colnod is finised
|
// change in colnod is newer
|
||||||
void SyncManager::areDataUpdatedToColnod()
|
if (databaseData->updatedTokens.value(colnodToken->id()) < colnodToken) {
|
||||||
{
|
preparedColnodData->updatedTokens.insert(colnodToken->id(), colnodToken);
|
||||||
if (deleteFinished && updateFinished)
|
}
|
||||||
{
|
else {
|
||||||
emit dataUpdatedToColnod();
|
conflicts.append(colnodToken->id());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto &id : colnodData->deletedTokens) {
|
||||||
|
auto corespondingToken = database->getToken(id);
|
||||||
|
if ( !corespondingToken) {
|
||||||
|
qWarning(syncmanager) << "Token:" << id << ", deleted in colnod, but not exists in db";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !corespondingToken->deleted()) {
|
||||||
|
preparedColnodData->deletedTokens.append(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preparedColnodData->updatedSubjects = colnodData->updatedSubjects;
|
||||||
|
preparedColnodData->deletedSubjects = colnodData->deletedSubjects;
|
||||||
|
|
||||||
|
qInfo(syncmanager) << tab << "- prep col:" << tab << "tokens [" << preparedColnodData->updatedTokens.size() << ":" << preparedColnodData->deletedTokens.size() <<
|
||||||
|
"] subjects [" << preparedColnodData->updatedSubjects.size() << ":" << preparedColnodData->deletedSubjects.size() << "] [ added/updated : deleted ]";
|
||||||
|
|
||||||
|
|
||||||
|
auto preparedData = std::make_unique<PreparedData>(preparedDatabaseData, preparedColnodData);
|
||||||
|
return preparedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncManager::initNewToken(const std::shared_ptr<Token>& token)
|
||||||
|
{
|
||||||
|
Q_ASSERT(database);
|
||||||
|
Q_ASSERT(token);
|
||||||
|
qint16 num = database->getNumOfTokensOfSubject(token->subjectId());
|
||||||
|
token->setName(database->getSubjectNameByTokenId(token->id()) + "_mobile_" + QString::number(num + 1));
|
||||||
|
token->setDescription("mobile");
|
||||||
|
token->setTokenData(token->tokenData().toUpper());
|
||||||
|
|
||||||
|
qInfo(syncmanager).noquote() << Message::massageTemplate("Inicializing new", "token", "sync manager", token->name());
|
||||||
|
}
|
||||||
|
|||||||
@ -1,59 +1,42 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "requestmanager.h"
|
#include "colnodapi.h"
|
||||||
#include "datamanager.h"
|
#include "databaseapi.h"
|
||||||
#include "logger.h"
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(syncmanager)
|
||||||
|
|
||||||
class SyncManager : public QObject
|
class SyncManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
friend class SyncManagerTest;
|
||||||
public:
|
public:
|
||||||
explicit SyncManager(bool dryMode = false);
|
explicit SyncManager(bool dryMode);
|
||||||
|
|
||||||
void syncDataFromColnodToDB();
|
|
||||||
void syncDataFromDBToColnod();
|
|
||||||
|
|
||||||
void dropTables();
|
|
||||||
void createTables();
|
|
||||||
|
|
||||||
bool isValid();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void loadDataFromColnod();
|
|
||||||
void loadDataFromDB();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void dataDownloaded();
|
|
||||||
void dataUpdatedToColnod();
|
|
||||||
void syncFinished();
|
|
||||||
void error();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
|
|
||||||
|
bool databaseInitialization();
|
||||||
|
bool startSynchronization();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadTokensFromColnod();
|
/**
|
||||||
void loadUpdatedDataFromColnod();
|
* @brief Preapre data to be pushed to database and colnod from actual downloaded data
|
||||||
void loadDeletedDataFromColnod();
|
* @param data from database
|
||||||
void loadUpdatedDataFromDB();
|
* @param data from colnod
|
||||||
void loadDeletedDataFromDB();
|
* @return prepared data to be pushed into colnod and db
|
||||||
|
*/
|
||||||
void syncUpdatedDataFromColnodToDB();
|
std::unique_ptr<PreparedData> prepareData(const std::shared_ptr<Data> &databaseData, const std::shared_ptr<Data> &colnodData);
|
||||||
void syncDeletedDataFromColnodToDB();
|
|
||||||
|
|
||||||
void syncUpdatedDataFromDBToColnod();
|
|
||||||
void syncDeletedDataFromDBToColnod();
|
|
||||||
|
|
||||||
void areChangedDataReady();
|
|
||||||
void areDataUpdatedToColnod();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<RequestManager> reqMan;
|
/**
|
||||||
std::unique_ptr<DataManager> dataMan;
|
* @brief Set default values to new tokens from db
|
||||||
bool dryMode = false;
|
* @param token New token in this run
|
||||||
bool updateFinished = false;
|
*/
|
||||||
bool deleteFinished = false;
|
void initNewToken(const std::shared_ptr<Token>& token);
|
||||||
int dataCounter = 0;
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<ColnodAPI> colnod;
|
||||||
|
std::shared_ptr<DatabaseAPI> database;
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
serversdk::DatabaseManager dbMan;
|
||||||
|
Timestamp mlastSync;
|
||||||
|
|
||||||
|
bool mDryMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,137 +1,84 @@
|
|||||||
#include "token.h"
|
#include "token.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <qjsondocument.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
Token::Token(const QString& name, QString tokenData, const QString& decription)
|
|
||||||
{
|
|
||||||
this->setName(name);
|
|
||||||
this->tokenData = std::move(tokenData);
|
|
||||||
this->setDescription(decription);
|
|
||||||
this->setId(QUuid::createUuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(const QString &name)
|
|
||||||
{
|
|
||||||
this->setName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::~Token()
|
Token::~Token()
|
||||||
= default;
|
= default;
|
||||||
|
|
||||||
|
|
||||||
void Token::fromJSON(const QJsonObject &json) {
|
QByteArray Token::tokensToColnodJson(const QHash<QString, std::shared_ptr<Token>> &tokens)
|
||||||
Entity::fromJSON(json);
|
|
||||||
|
|
||||||
tokenType = json["tokenType"].toString();
|
|
||||||
tokenBits = json["tokenBits"].toInt();
|
|
||||||
tokenData = json["tokenData"].toString();
|
|
||||||
timeToLive = json["timeToLive"].toInt();
|
|
||||||
authErrors = json["authErrors"].toInt();
|
|
||||||
const auto &flagsArray = json["flags"].toArray();
|
|
||||||
flags.clear();
|
|
||||||
for(const auto &flag : flagsArray){
|
|
||||||
flags.insert(flag.toString());
|
|
||||||
}
|
|
||||||
subjectId = json["subjectId"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject Token::toJSON() const {
|
|
||||||
QJsonObject json = Entity::toJSON();
|
|
||||||
json.insert("tokenType", tokenType);
|
|
||||||
json["tokenBits"] = tokenBits;
|
|
||||||
json["tokenType"] = tokenType;
|
|
||||||
json["tokenBits"] = tokenBits;
|
|
||||||
json["tokenData"] = tokenData;
|
|
||||||
json["timeToLive"] = timeToLive;
|
|
||||||
json["authErrors"] = authErrors;
|
|
||||||
json["flags"] = QJsonArray::fromStringList(flags.toList());
|
|
||||||
json["subjectId"] = subjectId.toString().mid(1,36);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Token::fromSQLRecord(const QSqlQuery& query)
|
|
||||||
{
|
{
|
||||||
setId(query.value(0).toUuid());
|
QJsonObject items;
|
||||||
authErrors = query.value(1).toInt();
|
QJsonArray jsonTokens;
|
||||||
setDescription(query.value(2).toString());
|
for (const auto& token : tokens)
|
||||||
QSet<QString> flags;
|
{
|
||||||
if (query.value(6).toBool())
|
jsonTokens.push_back(QJsonValue(QJsonDocument::fromJson(token->jsonData()).object()));
|
||||||
flags.insert("NoValidity");
|
}
|
||||||
if (query.value(3).toBool())
|
items.insert("items", jsonTokens);
|
||||||
flags.insert("Enabled");
|
QJsonDocument doc;
|
||||||
this->flags = flags;
|
doc.setObject(items);
|
||||||
QDateTime date = query.value(4).toDateTime();
|
return doc.toJson();
|
||||||
setLastUpdate(date.toMSecsSinceEpoch());
|
|
||||||
setName(query.value(5).toString());
|
|
||||||
timeToLive = query.value(7).toInt();
|
|
||||||
tokenBits = query.value(8).toInt();
|
|
||||||
tokenData = query.value(9).toString();
|
|
||||||
tokenType = query.value(10).toString();
|
|
||||||
subjectId = query.value(11).toUuid();
|
|
||||||
date = query.value(12).toDateTime();
|
|
||||||
setLastModified(date.toMSecsSinceEpoch());
|
|
||||||
setDeleted(query.value(13).toBool());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &Token::getTokenType() const {
|
QByteArray Token::jsonData(const QSet<QString> &filters) const
|
||||||
return tokenType;
|
{
|
||||||
|
auto variantMap = this->toVariantMap(filters);
|
||||||
|
auto enabled = variantMap["enabled"].toBool();
|
||||||
|
auto noValidity = variantMap["noValidity"].toBool();
|
||||||
|
variantMap.remove("enabled");
|
||||||
|
variantMap.remove("noValidity");
|
||||||
|
variantMap.remove("lastModified");
|
||||||
|
variantMap.remove("deleted");
|
||||||
|
variantMap.remove("lastUpdate");
|
||||||
|
|
||||||
|
QVariantList flags;
|
||||||
|
if ( enabled ) {
|
||||||
|
flags << "Enabled";
|
||||||
|
}
|
||||||
|
if ( noValidity ) {
|
||||||
|
flags << "NoValidity";
|
||||||
|
}
|
||||||
|
variantMap.insert("flags", flags);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromVariant(variantMap);
|
||||||
|
return doc.toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Token::setTokenType(const QString &tokenType) {
|
bool Token::fromJson(const QByteArray &data)
|
||||||
Token::tokenType = tokenType;
|
{
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
QVariantMap variant = qvariant_cast<QVariantMap>(doc.toVariant());
|
||||||
|
|
||||||
|
QVariantList flags = variant["flags"].toList();
|
||||||
|
for (const auto &value : flags) {
|
||||||
|
if ( value.toString() == "Enabled") {
|
||||||
|
variant.insert("enabled", true);
|
||||||
|
}
|
||||||
|
if ( value.toString() == "NoValidity") {
|
||||||
|
variant.insert("noValidity", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variant.remove("flags");
|
||||||
|
auto lastUpdate = variant["lastUpdate"].toLongLong();
|
||||||
|
variant["lastUpdate"] = QDateTime::fromMSecsSinceEpoch(lastUpdate);
|
||||||
|
variant.insert("lastModified", QDateTime::fromMSecsSinceEpoch(lastUpdate));
|
||||||
|
// Populate this object data
|
||||||
|
bool res = fromVariantMap(variant);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Token::getTokenBits() const {
|
QByteArray Token::idsToJSON(const QList<QString> &ids)
|
||||||
return tokenBits;
|
{
|
||||||
}
|
QJsonArray jsonIds;
|
||||||
|
for (const auto& id : ids)
|
||||||
void Token::setTokenBits(int tokenBits) {
|
{
|
||||||
Token::tokenBits = tokenBits;
|
jsonIds.push_back(id);
|
||||||
}
|
}
|
||||||
|
QJsonObject items;
|
||||||
const QString &Token::getTokenData() const {
|
items.insert("items", jsonIds);
|
||||||
return tokenData;
|
QJsonDocument doc;
|
||||||
}
|
doc.setObject(items);
|
||||||
|
return doc.toJson();
|
||||||
void Token::setTokenData(const QString &tokenData) {
|
|
||||||
Token::tokenData = tokenData;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Token::getTimeToLive() const {
|
|
||||||
return timeToLive;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Token::setTimeToLive(int timeToLive) {
|
|
||||||
Token::timeToLive = timeToLive;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Token::getAuthErrors() const {
|
|
||||||
return authErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Token::setAuthErrors(int authErrors) {
|
|
||||||
Token::authErrors = authErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QSet<QString> &Token::getFlags() const {
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Token::setFlags(const QSet<QString> &flags) {
|
|
||||||
Token::flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QUuid &Token::getSubjectId() const {
|
|
||||||
return subjectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Token::setSubjectId(const QUuid &subjectId) {
|
|
||||||
Token::subjectId = subjectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Token::subjectIdToStringWithoutBraces(){
|
|
||||||
return subjectId.toString().mid(1,36);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,80 +1,75 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
#include <QSet>
|
|
||||||
#include <qsqlquery.h>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include "serversdk/databasetable.h"
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
class Token : public serversdk::DatabaseTable {
|
||||||
|
Q_OBJECT
|
||||||
|
TABLE_PROPERTY(QString, id, setId, QString())
|
||||||
|
TABLE_PROPERTY(int, authErrors, setAuthErrors, 255)
|
||||||
|
TABLE_PROPERTY(QString, description, setDescription, "")
|
||||||
|
TABLE_PROPERTY(bool, enabled, setEnabled, true)
|
||||||
|
TABLE_PROPERTY(QDateTime, lastUpdate, setLastUpdate, QDateTime())
|
||||||
|
TABLE_PROPERTY(QString, name, setName, "")
|
||||||
|
TABLE_PROPERTY(bool, noValidity, setNoValidity, true)
|
||||||
|
TABLE_PROPERTY(int, timeToLive, setTimeToLive, 255)
|
||||||
|
TABLE_PROPERTY(int, tokenBits, setTokenBits, 32)
|
||||||
|
TABLE_PROPERTY(QString, tokenData, setTokenData, QString())
|
||||||
|
TABLE_PROPERTY(QString, tokenType, setTokenType, "Token")
|
||||||
|
TABLE_PROPERTY(QString, subjectId, setSubjectId, QString())
|
||||||
|
TABLE_PROPERTY(QDateTime, lastModified, setLastModified, QDateTime())
|
||||||
|
TABLE_PROPERTY(bool, deleted, setDeleted, false)
|
||||||
|
|
||||||
class Token : public Entity {
|
|
||||||
public:
|
public:
|
||||||
// using Entity::Entity;
|
/**
|
||||||
|
* # Needed values to proper sync
|
||||||
|
*
|
||||||
|
* * id - primary key
|
||||||
|
* * lastModified - when it was edited/modified
|
||||||
|
* * tokenData - key to open doors
|
||||||
|
* * subjectId - no way of make unassinged token (token without subject)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
explicit Token() = default;
|
||||||
* # Needed values to proper sync
|
|
||||||
*
|
|
||||||
* * id - primary key
|
|
||||||
* * lastModified - when it was edited/modified
|
|
||||||
* * tokenData - key to open doors
|
|
||||||
* * subjectId - no way of make unassinged token (token without subject)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
Token() = default;
|
|
||||||
Token(const QString& name, QString tokenData, const QString& decription = "mobile");
|
|
||||||
explicit Token(const QString& name);
|
|
||||||
~Token() override;
|
~Token() override;
|
||||||
|
|
||||||
friend bool operator<(const Token &lToken, const Token &rToken){
|
QByteArray jsonData(const QSet<QString> &filters = {}) const override;
|
||||||
return lToken.getLastModified() < rToken.getLastUpdate();
|
bool fromJson(const QByteArray &data) override;
|
||||||
|
|
||||||
|
friend bool operator<(const std::shared_ptr<Token> &lToken, const std::shared_ptr<Token> &rToken){
|
||||||
|
return lToken->lastModified() < rToken->lastUpdate();
|
||||||
}
|
}
|
||||||
friend bool operator>(const Token &lToken, const Token &rToken){
|
friend bool operator>(const std::shared_ptr<Token> &lToken, const std::shared_ptr<Token> &rToken){
|
||||||
return lToken.getLastModified() > rToken.getLastUpdate();
|
return lToken->lastModified() > rToken->lastUpdate();
|
||||||
}
|
|
||||||
friend bool operator==(const Token &lToken, const Token &rToken){
|
|
||||||
return lToken.getId() == rToken.getId() &&
|
|
||||||
lToken.getName() == rToken.getName() &&
|
|
||||||
lToken.getDescription() == rToken.getDescription() &&
|
|
||||||
lToken.getLastUpdate() == rToken.getLastUpdate() &&
|
|
||||||
lToken.tokenType == rToken.tokenType &&
|
|
||||||
lToken.tokenBits == rToken.tokenBits &&
|
|
||||||
lToken.tokenData == rToken.tokenData &&
|
|
||||||
lToken.timeToLive == rToken.timeToLive &&
|
|
||||||
lToken.authErrors == rToken.authErrors &&
|
|
||||||
lToken.flags == rToken.flags &&
|
|
||||||
lToken.subjectId == rToken.subjectId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare of two tokens
|
||||||
|
* @return Returns true, if tokens are same
|
||||||
|
*/
|
||||||
|
friend bool operator==(const std::shared_ptr<Token> &lToken, const std::shared_ptr<Token> &rToken)
|
||||||
|
{
|
||||||
|
return lToken->id() == rToken->id() &&
|
||||||
|
lToken->name() == rToken->name() &&
|
||||||
|
lToken->description() == rToken->description() &&
|
||||||
|
lToken->lastUpdate() == rToken->lastUpdate() &&
|
||||||
|
lToken->tokenType() == rToken->tokenType() &&
|
||||||
|
lToken->tokenBits() == rToken->tokenBits() &&
|
||||||
|
lToken->tokenData() == rToken->tokenData() &&
|
||||||
|
lToken->timeToLive() == rToken->timeToLive() &&
|
||||||
|
lToken->authErrors() == rToken->authErrors() &&
|
||||||
|
lToken->noValidity() == rToken->noValidity() &&
|
||||||
|
lToken->enabled() == rToken->enabled() &&
|
||||||
|
lToken->subjectId() == rToken->subjectId();
|
||||||
|
}
|
||||||
|
|
||||||
const QString &getTokenType() const;
|
static QByteArray tokensToColnodJson(const QHash<QString, std::shared_ptr<Token> > &tokens);
|
||||||
void setTokenType(const QString &tokenType);
|
static QByteArray idsToJSON(const QList<QString> &ids);
|
||||||
int getTokenBits() const;
|
|
||||||
void setTokenBits(int tokenBits);
|
|
||||||
const QString &getTokenData() const;
|
|
||||||
void setTokenData(const QString &tokenData);
|
|
||||||
int getTimeToLive() const;
|
|
||||||
void setTimeToLive(int timeToLive);
|
|
||||||
int getAuthErrors() const;
|
|
||||||
void setAuthErrors(int authErrors);
|
|
||||||
const QSet<QString> &getFlags() const;
|
|
||||||
void setFlags(const QSet<QString> &flags);
|
|
||||||
const QUuid &getSubjectId() const;
|
|
||||||
void setSubjectId(const QUuid &subjectId);
|
|
||||||
|
|
||||||
|
|
||||||
QString subjectIdToStringWithoutBraces();
|
|
||||||
|
|
||||||
void fromSQLRecord(const QSqlQuery& query);
|
|
||||||
void fromJSON(const QJsonObject &json) override;
|
|
||||||
QJsonObject toJSON() const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString tokenType = "Token";
|
|
||||||
int tokenBits = 32;
|
|
||||||
QString tokenData;
|
|
||||||
int timeToLive = 255;
|
|
||||||
int authErrors = 255;
|
|
||||||
QSet<QString> flags = {"NoValidity", "Enabled"};
|
|
||||||
QUuid subjectId;
|
|
||||||
};
|
};
|
||||||
|
|||||||
6
test/auto/auto.pro
Normal file
6
test/auto/auto.pro
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS += \
|
||||||
|
database/tst_databaseapi.pro \
|
||||||
|
colnod/tst_colnodapi.pro \
|
||||||
|
syncmanager/tst_syncmanager.pro \
|
||||||
101
test/auto/colnod/tst_colnodapi.cpp
Normal file
101
test/auto/colnod/tst_colnodapi.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "colnodapi.h"
|
||||||
|
#include "serversdk/request.h"
|
||||||
|
// add necessary includes here
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(std::shared_ptr<Data>)
|
||||||
|
Q_DECLARE_METATYPE(std::shared_ptr<serversdk::Request>);
|
||||||
|
|
||||||
|
|
||||||
|
class ColnodTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ColnodTest()
|
||||||
|
{
|
||||||
|
qRegisterMetaType<std::shared_ptr<Data>>();
|
||||||
|
qRegisterMetaType<std::shared_ptr<serversdk::Request>>("std::shared_ptr<Request>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void login();
|
||||||
|
void download_data_test();
|
||||||
|
void download_data_test_now();
|
||||||
|
void download_data_fail_test();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getLoginToken(ColnodAPI &colnod);
|
||||||
|
};
|
||||||
|
|
||||||
|
void ColnodTest::login()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
ColnodAPI colnod(&reqMan);
|
||||||
|
auto login = colnod.requestLogin();
|
||||||
|
QSignalSpy spy(login.get(), &serversdk::Request::finished);
|
||||||
|
QCOMPARE(spy.count(), 0);
|
||||||
|
QVERIFY(spy.wait());
|
||||||
|
QCOMPARE(spy.count(), 1);
|
||||||
|
QVERIFY(login->responseData().contains("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColnodTest::download_data_test()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
//reqMan.setIgnoreSsl(true);
|
||||||
|
ColnodAPI colnod(&reqMan);
|
||||||
|
|
||||||
|
QSignalSpy spy(&colnod, &ColnodAPI::downloadDataFinished);
|
||||||
|
QVERIFY(spy.isValid());
|
||||||
|
colnod.downloadData(1570190399259);
|
||||||
|
QVERIFY(spy.wait());
|
||||||
|
|
||||||
|
auto data = qvariant_cast<std::shared_ptr<Data>>(spy.at(0).at(0));
|
||||||
|
QVERIFY(data);
|
||||||
|
|
||||||
|
QVERIFY(! data->updatedTokens.isEmpty());
|
||||||
|
QVERIFY(data->updatedTokens.contains("d3a83bea-2587-4687-946d-ee847eccf477"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColnodTest::download_data_test_now()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
ColnodAPI colnod(&reqMan);
|
||||||
|
|
||||||
|
colnod.downloadData(QDateTime::currentMSecsSinceEpoch());
|
||||||
|
QSignalSpy spy(&colnod, &ColnodAPI::downloadDataFinished);
|
||||||
|
QVERIFY(spy.wait());
|
||||||
|
auto data = qvariant_cast<std::shared_ptr<Data>>(spy.at(0).at(0));
|
||||||
|
QVERIFY(data);
|
||||||
|
|
||||||
|
QVERIFY(data->updatedTokens.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColnodTest::download_data_fail_test()
|
||||||
|
{
|
||||||
|
serversdk::RequestManager reqMan;
|
||||||
|
ColnodAPI colnod(&reqMan, false, "spaceti", "wrong password");
|
||||||
|
|
||||||
|
QSignalSpy spy(&colnod, &ColnodAPI::downloadDataFinished);
|
||||||
|
colnod.downloadData(0);
|
||||||
|
|
||||||
|
QVERIFY(spy.wait());
|
||||||
|
|
||||||
|
auto data = qvariant_cast<std::shared_ptr<Data>>(spy.at(0).at(0));
|
||||||
|
|
||||||
|
QVERIFY(!data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ColnodTest::getLoginToken(ColnodAPI &colnod)
|
||||||
|
{
|
||||||
|
auto login = colnod.requestLogin();
|
||||||
|
QSignalSpy spy(login.get(), &serversdk::Request::finished);
|
||||||
|
spy.wait();
|
||||||
|
return login->responseData().mid(7,36);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(ColnodTest)
|
||||||
|
|
||||||
|
#include "tst_colnodapi.moc"
|
||||||
11
test/auto/colnod/tst_colnodapi.pro
Normal file
11
test/auto/colnod/tst_colnodapi.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_colnodapi.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
114
test/auto/database/tst_databaseapi.cpp
Normal file
114
test/auto/database/tst_databaseapi.cpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "databaseapi.h"
|
||||||
|
// add necessary includes here
|
||||||
|
#include "token.h"
|
||||||
|
|
||||||
|
class DatabaseTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void open_database_test();
|
||||||
|
void open_database_ip_fail_test();
|
||||||
|
void open_database_name_fail_test();
|
||||||
|
void open_database_user_fail_test();
|
||||||
|
void open_database_password_fail_test();
|
||||||
|
void download_data_test();
|
||||||
|
void download_data_fail_test();
|
||||||
|
void get_token_test();
|
||||||
|
};
|
||||||
|
|
||||||
|
void DatabaseTest::open_database_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
QVERIFY(db.open());
|
||||||
|
|
||||||
|
// TODO: fix -> databaseAPI must be able to add credentials to check for wrong one or test wrong IP
|
||||||
|
//db.open(user, password, url, etc );
|
||||||
|
// then test with wrong credentials
|
||||||
|
// QVERIFY(!db.open(worng usernae));
|
||||||
|
// QVERIFY(!db.open(worng ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::open_database_ip_fail_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
QVERIFY(!db.open("1.1.1.1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::open_database_name_fail_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
QVERIFY(!db.open("192.168.1.94", "wrong name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::open_database_user_fail_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
QVERIFY(!db.open("192.168.1.94", "delliote", "wrong user"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::open_database_password_fail_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
QVERIFY(!db.open("192.168.1.94", "delliote", "postgres", "wrong password"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DatabaseTest::download_data_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
db.open();
|
||||||
|
QVERIFY(db.downloadData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::download_data_fail_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
|
||||||
|
// fail open so download data should return nullptr
|
||||||
|
db.open("1.1.1.1");
|
||||||
|
QVERIFY(!db.downloadData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTest::get_token_test()
|
||||||
|
{
|
||||||
|
serversdk::DatabaseManager dbManager;
|
||||||
|
DatabaseAPI db(&dbManager, false);
|
||||||
|
{
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "QSqlQuery::prepare: database not open");
|
||||||
|
QTest::ignoreMessage(QtSystemMsg, "ERROR during accessing existing tokens. EXIT");
|
||||||
|
QVERIFY(!db.getToken("6c907a42-0401-4403-8af4-9d58eb35cddc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
db.open();
|
||||||
|
|
||||||
|
auto token = db.getToken("6c907a42-0401-4403-8af4-9d58eb35cddc");
|
||||||
|
QVERIFY(token);
|
||||||
|
auto compareToken = std::make_shared<Token>();
|
||||||
|
compareToken->setId("6c907a42-0401-4403-8af4-9d58eb35cddc");
|
||||||
|
compareToken->setName("Losenicky");
|
||||||
|
compareToken->setDescription("");
|
||||||
|
compareToken->setLastUpdate(QDateTime::fromMSecsSinceEpoch(1543494713260));
|
||||||
|
compareToken->setTokenData("AB1EA4D4");
|
||||||
|
compareToken->setSubjectId("69c6e3b3-3f23-456f-a6c2-73ef08e589a4");
|
||||||
|
QCOMPARE(compareToken, token);
|
||||||
|
|
||||||
|
auto compareToken1 = std::make_shared<Token>();
|
||||||
|
auto token1 = db.getToken("non-existing-id");
|
||||||
|
QVERIFY(token1);
|
||||||
|
QCOMPARE(compareToken1, token1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(DatabaseTest)
|
||||||
|
|
||||||
|
#include "tst_databaseapi.moc"
|
||||||
11
test/auto/database/tst_databaseapi.pro
Normal file
11
test/auto/database/tst_databaseapi.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_databaseapi.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
154
test/auto/syncmanager/tst_syncmanager.cpp
Normal file
154
test/auto/syncmanager/tst_syncmanager.cpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "syncmanager.h"
|
||||||
|
// add necessary includes here
|
||||||
|
|
||||||
|
class SyncManagerTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Token> tokenFromString(const QString &text);
|
||||||
|
private slots:
|
||||||
|
void tokenFromString_test();
|
||||||
|
void sync_init_test();
|
||||||
|
void prepareData_test();
|
||||||
|
void prepareData_tokens();
|
||||||
|
void prepareData_tokens_data();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<Token> SyncManagerTest::tokenFromString(const QString &text)
|
||||||
|
{
|
||||||
|
auto split = text.split(",");
|
||||||
|
if (split.count() != 2)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto token = std::make_shared<Token>();
|
||||||
|
token->setId(split.first());
|
||||||
|
token->setDescription("mobile");
|
||||||
|
token->setLastUpdate(QDateTime::fromString(split.last(), "yyyyMMdd:HHmmss"));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManagerTest::tokenFromString_test()
|
||||||
|
{
|
||||||
|
auto token = tokenFromString("id1,20191010:232300");
|
||||||
|
QCOMPARE(token->id(), QStringLiteral("id1"));
|
||||||
|
QCOMPARE(token->lastUpdate(), QDateTime::fromString("20191010:232300", "yyyyMMdd:HHmmss"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManagerTest::sync_init_test()
|
||||||
|
{
|
||||||
|
SyncManager sync(true);
|
||||||
|
QVERIFY(sync.databaseInitialization());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SyncManagerTest::prepareData_test()
|
||||||
|
{
|
||||||
|
SyncManager sync(true);
|
||||||
|
QVERIFY(sync.databaseInitialization());
|
||||||
|
auto dbData = std::make_shared<Data>();
|
||||||
|
auto colnodData = std::make_shared<Data>();
|
||||||
|
|
||||||
|
auto token = std::make_shared<Token>();
|
||||||
|
token->setDescription("mobile");
|
||||||
|
token->setId("id1");
|
||||||
|
dbData->updatedTokens.insert("id1", token);
|
||||||
|
|
||||||
|
auto newPreparedData = sync.prepareData(dbData, colnodData);
|
||||||
|
QVERIFY(newPreparedData->databaseData->updatedTokens.contains("id1"));
|
||||||
|
QVERIFY(newPreparedData->databaseData->updatedSubjects.isEmpty());
|
||||||
|
QVERIFY(newPreparedData->databaseData->deletedTokens.isEmpty());
|
||||||
|
QVERIFY(newPreparedData->databaseData->deletedSubjects.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManagerTest::prepareData_tokens()
|
||||||
|
{
|
||||||
|
SyncManager sync(true);
|
||||||
|
QVERIFY(sync.databaseInitialization());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QFETCH(QString, db_token_updated);
|
||||||
|
QFETCH(QString, db_token_deleted);
|
||||||
|
QFETCH(QString, colnod_token_updated);
|
||||||
|
QFETCH(QString, colnod_token_deleted);
|
||||||
|
QFETCH(QString, expected_db_tokens_toupdate);
|
||||||
|
QFETCH(QString, expected_db_tokens_todelete);
|
||||||
|
QFETCH(QString, expected_colnod_tokens_toupdate);
|
||||||
|
QFETCH(QString, expected_colnod_tokens_todelete);
|
||||||
|
|
||||||
|
// fake data
|
||||||
|
auto dbData = std::make_shared<Data>();
|
||||||
|
auto colnodData = std::make_shared<Data>();
|
||||||
|
|
||||||
|
// Database
|
||||||
|
auto utoken = tokenFromString(db_token_updated);
|
||||||
|
if ( utoken )
|
||||||
|
dbData->updatedTokens.insert(utoken->id(), utoken);
|
||||||
|
auto dtoken = tokenFromString(db_token_deleted);
|
||||||
|
if ( dtoken )
|
||||||
|
dbData->deletedTokens.append(dtoken->id());
|
||||||
|
|
||||||
|
// Colnod
|
||||||
|
auto colnod_utoken = tokenFromString(colnod_token_updated);
|
||||||
|
if ( colnod_utoken )
|
||||||
|
colnodData->updatedTokens.insert(colnod_utoken->id(), colnod_utoken);
|
||||||
|
auto colnod_dtoken = tokenFromString(colnod_token_deleted);
|
||||||
|
if ( colnod_dtoken )
|
||||||
|
colnodData->deletedTokens.append(colnod_dtoken->id());
|
||||||
|
|
||||||
|
auto newPreparedData = sync.prepareData(dbData, colnodData);
|
||||||
|
|
||||||
|
|
||||||
|
auto testTokens = [=](const QSet<QString> &set, const QString &expectedId) {
|
||||||
|
if (expectedId.isEmpty()) {
|
||||||
|
QVERIFY(set.isEmpty());
|
||||||
|
} else {
|
||||||
|
QVERIFY(set.contains(expectedId));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
testTokens(newPreparedData->databaseData->updatedTokens.keys().toSet(), expected_db_tokens_toupdate);
|
||||||
|
testTokens(newPreparedData->databaseData->deletedTokens.toSet(), expected_db_tokens_todelete);
|
||||||
|
|
||||||
|
testTokens(newPreparedData->colnodData->updatedTokens.keys().toSet(), expected_colnod_tokens_toupdate);
|
||||||
|
|
||||||
|
// Can't work without access to database
|
||||||
|
//testTokens(newPreparedData->colnodData->deletedTokens.toSet(), expected_colnod_tokens_todelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManagerTest::prepareData_tokens_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("db_token_updated");
|
||||||
|
QTest::addColumn<QString>("db_token_deleted");
|
||||||
|
QTest::addColumn<QString>("colnod_token_updated");
|
||||||
|
QTest::addColumn<QString>("colnod_token_deleted");
|
||||||
|
QTest::addColumn<QString>("expected_db_tokens_toupdate");
|
||||||
|
QTest::addColumn<QString>("expected_db_tokens_todelete");
|
||||||
|
QTest::addColumn<QString>("expected_colnod_tokens_toupdate");
|
||||||
|
QTest::addColumn<QString>("expected_colnod_tokens_todelete");
|
||||||
|
|
||||||
|
// in: db 1 new token
|
||||||
|
// out: 1 db token must be updated in colnod
|
||||||
|
QTest::newRow("db 1 update") << "id1,20191010:23:23:00" << ""
|
||||||
|
<< "" << ""
|
||||||
|
// expected database: toupdate << todelete
|
||||||
|
<< "id1" << ""
|
||||||
|
// expected colnod: toupdate << todelete
|
||||||
|
<< "" << "";
|
||||||
|
// in: id1 deleted in colnod
|
||||||
|
// out: 1 colnod token must be deleted from database
|
||||||
|
QEXPECT_FAIL("delete 1 existing token in database", "Rare case, not sure if handling is needed", Continue);
|
||||||
|
QTest::newRow("delete 1 existing token in database") << "" << ""
|
||||||
|
<< "" << "id1,20191010:23:23:00"
|
||||||
|
// expected database: toupdate << todelete
|
||||||
|
<< "" << ""
|
||||||
|
// expected colnod: toupdate << todelete
|
||||||
|
<< "" << "id1";
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(SyncManagerTest)
|
||||||
|
|
||||||
|
#include "tst_syncmanager.moc"
|
||||||
11
test/auto/syncmanager/tst_syncmanager.pro
Normal file
11
test/auto/syncmanager/tst_syncmanager.pro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
QT += testlib
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_syncmanager.cpp
|
||||||
|
|
||||||
|
include(../../test.pri)
|
||||||
34
test/test.cpp
Normal file
34
test/test.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return path to source directory
|
||||||
|
* @param subpath ( e.g. test/data )
|
||||||
|
* @return absolute path to desired subpath in source folder
|
||||||
|
*/
|
||||||
|
QString Test::sourcePath(const QString &subpath)
|
||||||
|
{
|
||||||
|
const QString path = QStringLiteral(COLNOD_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();
|
||||||
|
}
|
||||||
12
test/test.h
Normal file
12
test/test.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QString sourcePath(const QString &subpath);
|
||||||
|
static QByteArray sourceData(const QString &subpath);
|
||||||
|
};
|
||||||
|
|
||||||
16
test/test.pri
Normal file
16
test/test.pri
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
COLNOD_ROOT=$$PWD/..
|
||||||
|
COLNOD_TEST_PATH=$$SDK_ROOT/test/data
|
||||||
|
|
||||||
|
DEFINES += COLNOD_TEST_PATH='\\"$$COLNOD_TEST_PATH\\"'
|
||||||
|
|
||||||
|
QT += testlib
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/test.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/test.cpp
|
||||||
|
|
||||||
|
include(../src/colnod/colnod.pri)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
unitTests
|
auto
|
||||||
|
|
||||||
|
|||||||
@ -5,299 +5,6 @@ void UnitTest::initTestCase()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnitTest::loginSuccessfully()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::successLogin);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
testAccess.login();
|
|
||||||
spy.wait(timeout);
|
|
||||||
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = testAccess.jsonResponse;
|
|
||||||
qDebug() << json;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QVERIFY2(doc.object().value("id").isString(), "No parameter 'id' in response");
|
|
||||||
}
|
|
||||||
|
|
||||||
//POST request w/o datas
|
|
||||||
void UnitTest::getSubjects()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
Response* response = testAccess.getSubjects();
|
|
||||||
QSignalSpy spy1(response, &Response::ready);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QCOMPARE(spy1.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QVERIFY(doc.isObject());
|
|
||||||
QVERIFY2(doc.object().value("items").isArray(), "No parameter 'id' in response");
|
|
||||||
qDebug() << doc.object().value("items").toArray()[0].toObject()["id"].toString();
|
|
||||||
QVERIFY2(doc.object().value("items").toArray()[0].toObject()["id"].isString(), "ada");
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getTokens()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
Response* response = testAccess.getTokens();
|
|
||||||
QSignalSpy spy(response, &Response::ready);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QVERIFY(doc.isObject());
|
|
||||||
QVERIFY2(doc.object().value("items").isArray(), "No parameter 'id' in response");
|
|
||||||
qDebug() << doc.object().value("items").toArray()[0].toObject()["id"].toString();
|
|
||||||
QVERIFY2(doc.object().value("items").toArray()[0].toObject()["id"].isString(), "ada");
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getTokenSubject()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getTokenSubject();
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getSubjectsUpdated()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getSubjectsUpdated(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getSubjectsDeleted()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getSubjectsDeleted(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getTokensUpdated()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getTokensUpdated(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getTokensDeleted()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getTokensDeleted(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getAccessGroups()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getAccessGroups();
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getAccessGroupsUpdated()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getAccessGroupsUpdated(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getAccessGroupsDeleted()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getAccessGroupsDeleted(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getAccessGroupMembership()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getAccessGroupMembership();
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getAccessGroupMembershipUpdated()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getAccessGroupMembershipUpdated(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::getAccessGroupMembershipDeleted()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getAccessGroupMembershipDeleted(epochMS);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::setTokens()
|
|
||||||
{
|
|
||||||
|
|
||||||
QHash<QString, std::shared_ptr<Token>> tokens;
|
|
||||||
auto token = getTestToken();
|
|
||||||
tokens.insert(token->idToStringWithoutBraces(), token);
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.setTokens(tokens);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::deleteTokens()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
QList<QString> ids;
|
|
||||||
ids.append("582c982d-6fd7-4856-8548-7ce542791c7a");
|
|
||||||
ids.append("3ff23b41-3da3-4b33-82ce-d8e2d8c9a546");
|
|
||||||
Response* response = testAccess.deleteTokens(ids);
|
|
||||||
QCOMPARE(spy.count(), 0);
|
|
||||||
spy.wait(timeout);
|
|
||||||
QVERIFY(spy.isValid());
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QVERIFY(!json.isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::subjectJSONParser()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getSubjects();
|
|
||||||
spy.wait(timeout);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QJsonArray arr = doc.object()["items"].toArray();
|
|
||||||
Subject testSubject;
|
|
||||||
testSubject.fromJSON(arr[1].toObject());
|
|
||||||
qDebug() << testSubject.getName() << " Size: " << arr.size();
|
|
||||||
QVERIFY(!testSubject.getName().isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnitTest::tokenJSONParser()
|
|
||||||
{
|
|
||||||
RequestManager testAccess;
|
|
||||||
QSignalSpy spy(&testAccess, &RequestManager::responseReady);
|
|
||||||
Response* response = testAccess.getTokens();
|
|
||||||
spy.wait(timeout);
|
|
||||||
QString json = response->getJson();
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
QJsonArray arr = doc.object()["items"].toArray();
|
|
||||||
Token testToken;
|
|
||||||
testToken.fromJSON(arr[5].toObject());
|
|
||||||
qDebug() << testToken.getName() << " Size: " << arr.size();
|
|
||||||
QVERIFY(!testToken.getName().isEmpty());
|
|
||||||
response->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Token> UnitTest::getTestToken()
|
|
||||||
{
|
|
||||||
QJsonObject jsonToken;
|
|
||||||
jsonToken.insert("id", "062c93f6-ba3e-46af-a3a6-7a2988d1f0e9");
|
|
||||||
jsonToken.insert("name", "test");
|
|
||||||
jsonToken.insert("description", "mobile");
|
|
||||||
jsonToken.insert("tokenType", "Token");
|
|
||||||
jsonToken.insert("tokenBits", 32);
|
|
||||||
jsonToken.insert("tokenData", "7B9D32");
|
|
||||||
jsonToken.insert("timeToLive", 255);
|
|
||||||
jsonToken.insert("authErrors", 255);
|
|
||||||
QJsonArray flags;
|
|
||||||
flags.push_back("Enabled");
|
|
||||||
flags.push_back("NoValidity");
|
|
||||||
jsonToken.insert("flags", flags);
|
|
||||||
jsonToken.insert("subjectId", "72b37f7e-5761-4e6c-8e87-20c0bed0d904");
|
|
||||||
auto token = std::make_shared<Token>();
|
|
||||||
token->fromJSON(jsonToken);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTEST_MAIN(UnitTest)
|
QTEST_MAIN(UnitTest)
|
||||||
|
|||||||
@ -12,12 +12,24 @@ void ComplexTest::initTestCase()
|
|||||||
// deleteTokensRespSpy.wait(timeout);
|
// deleteTokensRespSpy.wait(timeout);
|
||||||
// QTest::qWait(2500);
|
// QTest::qWait(2500);
|
||||||
// Logger *logger = Logger::getLogger();
|
// Logger *logger = Logger::getLogger();
|
||||||
start = QDateTime::currentMSecsSinceEpoch()-2000;
|
//start = QDateTime::currentMSecsSinceEpoch()-2000;
|
||||||
api = new SyncManager();
|
//api = new SyncManager();
|
||||||
qDebug() << start;
|
qDebug() << start;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//void DatabaseTableTest::enum_test2()
|
||||||
|
//{
|
||||||
|
// auto test = R"({"id":"e2432439-41ed-4440-9c8e-40edb9989a15","name":"Pavel Kanka","description":"","lastUpdate":1565350519781,"attributes":{"items":{"Jmeno":"Pavel","Spolecnost":"Colsys","Prijmeni":"Kaňka"}},"tokenFlags":["NoPin"],"pin":"","pinFlags":["Access"]})";
|
||||||
|
// Subject subject;
|
||||||
|
// subject.fromJson(test);
|
||||||
|
|
||||||
|
// QCOMPARE(subject.name(), "Pavel Kanka");
|
||||||
|
// qDebug() << subject.name() << subject.lastUpdate() << QDateTime::fromString("2019-08-09 11:35:19.000", "yyyy-MM-dd hh:mm:ss.zzz");
|
||||||
|
// QCOMPARE(subject.lastUpdate(), QDateTime::fromString("2019-08-09 11:35:19", "yyyy-MM-dd hh:mm:ss.zzz"));
|
||||||
|
// qDebug() << subject.attrib.value(1)->toVariantMap();
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
void ComplexTest::cleanupTestCase()
|
void ComplexTest::cleanupTestCase()
|
||||||
{
|
{
|
||||||
@ -155,22 +167,24 @@ void ComplexTest::testingStuff()
|
|||||||
// HELPFUL_FUNCTIONS
|
// HELPFUL_FUNCTIONS
|
||||||
int ComplexTest::getNumOfSubjects()
|
int ComplexTest::getNumOfSubjects()
|
||||||
{
|
{
|
||||||
auto reqMan = new RequestManager;
|
// auto reqMan = new RequestManager;
|
||||||
Response* subjectsResp = reqMan->getSubjects();
|
// Response* subjectsResp = reqMan->getSubjects();
|
||||||
QSignalSpy subjectsRespSpy(subjectsResp, &Response::ready);
|
// QSignalSpy subjectsRespSpy(subjectsResp, &Response::ready);
|
||||||
subjectsRespSpy.wait(timeout);
|
// subjectsRespSpy.wait(timeout);
|
||||||
auto doc = getJsonDoc(subjectsResp->getJson());
|
// auto doc = getJsonDoc(subjectsResp->getJson());
|
||||||
return doc.object()["items"].toArray().size();
|
// return doc.object()["items"].toArray().size();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ComplexTest::getNumOfTokens()
|
int ComplexTest::getNumOfTokens()
|
||||||
{
|
{
|
||||||
auto reqMan = new RequestManager;
|
// auto reqMan = new RequestManager;
|
||||||
Response* tokensResp = reqMan->getTokens();
|
// Response* tokensResp = reqMan->getTokens();
|
||||||
QSignalSpy tokensRespSpy(tokensResp, &Response::ready);
|
// QSignalSpy tokensRespSpy(tokensResp, &Response::ready);
|
||||||
tokensRespSpy.wait(timeout);
|
// tokensRespSpy.wait(timeout);
|
||||||
auto doc = getJsonDoc(tokensResp->getJson());
|
// auto doc = getJsonDoc(tokensResp->getJson());
|
||||||
return doc.object()["items"].toArray().size();
|
// return doc.object()["items"].toArray().size();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonDocument ComplexTest::getJsonDoc(const QString &json)
|
QJsonDocument ComplexTest::getJsonDoc(const QString &json)
|
||||||
@ -195,7 +209,7 @@ std::shared_ptr<Token> ComplexTest::getTestToken()
|
|||||||
jsonToken.insert("flags", flags);
|
jsonToken.insert("flags", flags);
|
||||||
jsonToken.insert("subjectId", "72b37f7e-5761-4e6c-8e87-20c0bed0d904");
|
jsonToken.insert("subjectId", "72b37f7e-5761-4e6c-8e87-20c0bed0d904");
|
||||||
auto token = std::shared_ptr<Token>();
|
auto token = std::shared_ptr<Token>();
|
||||||
token->fromJSON(jsonToken);
|
//token->fromJSON(jsonToken);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,7 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
#include "response.h"
|
|
||||||
#include "requestmanager.h"
|
|
||||||
#include "syncmanager.h"
|
#include "syncmanager.h"
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
class ComplexTest : public QObject
|
class ComplexTest : public QObject
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,13 +3,9 @@
|
|||||||
|
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <requestmanager.h>
|
|
||||||
#include "subject.h"
|
#include "subject.h"
|
||||||
#include "databasemanager.h"
|
#include "databaseapi.h"
|
||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
#include "datamanager.h"
|
|
||||||
#include "response.h"
|
|
||||||
|
|
||||||
|
|
||||||
class UnitTest : public QObject
|
class UnitTest : public QObject
|
||||||
{
|
{
|
||||||
@ -19,24 +15,7 @@ private slots:
|
|||||||
|
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
|
||||||
void loginSuccessfully(); // /AaaManager/authSession
|
|
||||||
void getSubjects(); // /SubjectManager/getSubjects
|
|
||||||
void getTokens(); // /TokenManager/getTokens
|
|
||||||
void getTokenSubject(); // /OpenManager/getTokenSubject
|
|
||||||
void getSubjectsUpdated();
|
|
||||||
void getSubjectsDeleted();
|
|
||||||
void getTokensUpdated();
|
|
||||||
void getTokensDeleted();
|
|
||||||
void getAccessGroups();
|
|
||||||
void getAccessGroupsUpdated();
|
|
||||||
void getAccessGroupsDeleted();
|
|
||||||
void getAccessGroupMembership();
|
|
||||||
void getAccessGroupMembershipUpdated();
|
|
||||||
void getAccessGroupMembershipDeleted();
|
|
||||||
void setTokens();
|
|
||||||
void deleteTokens();
|
|
||||||
void subjectJSONParser();
|
|
||||||
void tokenJSONParser();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Token> getTestToken();
|
std::shared_ptr<Token> getTestToken();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user