colnod-connector/src/colnod/databaseapi.cpp
2019-12-02 14:30:43 +01:00

343 lines
12 KiB
C++

#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(QString host, QString name, QString user, 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);
// TODO: why emiting signal??
// this can return Strucutre
//emit downloadDataFinished(composeData(updatedQuery, deletedQuery));
}
bool DatabaseAPI::processData(const std::shared_ptr<Data> &data)
{
uploadTokens(data->updatedTokens);
uploadSubjects(data->updatedSubjects);
deleteItems(data->deletedTokens, "colnod_token");
deleteItems(data->deletedSubjects, "colnod_subject");
if ( !data->updatedTokens.isEmpty() ||
!data->updatedSubjects.isEmpty() ||
!data->deletedTokens.isEmpty() ||
!data->deletedSubjects.isEmpty()) {
qInfo(database) << dash;
}
qInfo(database) << tab << "-> DATA PROCESSED TO DATABASE:";
qInfo(database) << tab << tab << data->updatedTokens.size() << "TOKENS ADDED/UPDATED";
qInfo(database) << tab << tab << data->deletedTokens.size() << "TOKENS DELETED";
qInfo(database) << tab << tab << data->updatedSubjects.size() << "SUBJECTS ADDED/UPDATED";
qInfo(database) << tab << tab << data->deletedSubjects.size() << "SUBJECTS DELETED";
qInfo(database) << dash;
// TODO: add error handling
return true;
}
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 << "-> DATABASE DATA:";
qInfo(database) << tab << tab << data->updatedTokens.size() << "TOKENS WERE ADDED/UPDATED";
qInfo(database) << tab << tab << data->deletedTokens.size() << "TOKENS MARKED FOR DELETION";
qInfo(database) << dash;
return data;
}
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.";
emit error();
}
if (query.next()){
return query.value(0).toLongLong();
}
return -1;
}
void 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";
emit error();
}
}
void DatabaseAPI::reupdateUpdated(const QHash<QString, std::shared_ptr<Token>> &tokens)
{
auto query = mDbMan->createQuery();
for (const auto &token : tokens){
query.prepare("UPDATE colnod_token SET last_update = :last_update WHERE id = :id");
query.bindValue(":last_update", token->lastUpdate());
query.bindValue(":id", token->id());
if (!executeQuery(query)) {
qWarning(database) << "ERROR during updating last update of token:" << token->name() << ", it will be synced from Colnod to db in next sync. Last modified will be invalid";
}
}
qInfo(database) << equals;
qInfo(database) << "Synchronize last update in database with colnod";
qInfo(database) << equals;
}
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();
qDebug() << "Database time:" << QDateTime::fromMSecsSinceEpoch(query.value(0).toLongLong()).toString("yyyy-MM-dd hh:mm:ss.zzz");
qDebug() << "Current time:" << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
return query.value(0).toLongLong();
}
void DatabaseAPI::initNewTokens()
{
auto query = mDbMan->createQuery();
if (!executeQuery(query, "SELECT * FROM colnod_token WHERE name IS NULL")) {
qWarning(database) << "ERROR during getting uninitialized tokens";
}
QHash<QString, std::shared_ptr<Token>> tokensToInit;
auto list = utils->tableFromQuery<Token>(query);
for (const auto &token : list) {
qint16 num = getNumOfTokensOfSubject(token->subjectId());
token->setName(getSubjectNameByTokenId(token->id()) + "_mobile_" + QString::number(num + 1));
token->setDescription("mobile");
token->setTokenData(token->tokenData().toUpper());
tokensToInit.insert(token->id(), token);
qInfo(database).noquote() << Message::massageTemplate("Inicializing new", "token", "database", token->name());
}
uploadTokens(tokensToInit);
}
bool DatabaseAPI::uploadTokens(const QHash<QString, std::shared_ptr<Token>> &tokens)
{
// TODO: first check if OK, then message
// OK -> token was updated
// ERROR -> not possible to update
auto query = mDbMan->createQuery();
for (const auto &token : tokens) {
// Get token
if (exists(token->id(), "colnod_token")) {
query = utils->updateTable(token.get(), "colnod_token");
qInfo(database).noquote() << Message::massageTemplate("Updating", "token", "database", token->name());
}
else {
// TODO: first check if OK, then message
// OK -> token was updated
// ERROR -> not possible to update
query = utils->insertIntoTable(token.get(), "colnod_token");
qInfo(database).noquote() << Message::massageTemplate("Adding", "token", "database", token->name());
}
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");
qInfo(database).noquote() << Message::massageTemplate("Updating", "subject", "database", subject->name());
}
else {
query = utils->insertIntoTable(subject.get(), "colnod_subject");
qInfo(database).noquote() << Message::massageTemplate("Adding", "subject", "database", subject->name());
}
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");
for (const auto &id : ids) {
qInfo(database).noquote() << Message::massageTemplate("Deleting", tableName.mid(tableName.indexOf("_") + 1, tableName.length() - 1), "database", id);
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 token as deleted:" << id;
return false;
}
}
return true;
}
std::shared_ptr<Token> DatabaseAPI::getToken(const QString &id)
{
auto query = mDbMan->createQuery();
query.prepare("SELECT * FROM colnod_token WHERE d = :id");
query.bindValue(":id", id);
if (!executeQuery(query)) {
qCritical(database) << "ERROR during accessing existing tokens. EXIT";
emit error();
return nullptr;
}
auto list = utils->tableFromQuery<Token>(query);
if (list.isEmpty())
return nullptr;
return list.at(0);
}
bool DatabaseAPI::isAlreadyDeleted(const QString &id)
{
auto token = getToken(id);
if (!token) {
if (id.isEmpty()) {
}
qWarning(database) << "Token deleted in colnod doesn't exists in db, will be noted as already deleted in stats";
return true;
}
if (token->deleted())
return true;
return false;
}
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 deleted = false");
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)";
}