From d98ddf6c5fd03aef7c6eececd42d239220f3f9f6 Mon Sep 17 00:00:00 2001 From: Filip Bucek Date: Mon, 11 Nov 2019 18:04:56 +0100 Subject: [PATCH] Adding database Table class for automated handling tables --- serversdkint.pri | 1 + src/serversdk/databasetable.cpp | 61 ++++++++ src/serversdk/databasetable.h | 40 ++++++ src/serversdk/databaseutils.cpp | 134 ++++++++++++++++++ src/serversdk/databaseutils.h | 26 ++++ src/serversdk/serversdk.pro | 36 ++--- test/auto/auto.pro | 6 +- .../tst_databasemanager.cpp} | 2 +- test/auto/database/tst_databasemanager.pro | 11 ++ test/auto/database/tst_databasetable.cpp | 54 +++++++ .../tst_databasetable.pro} | 2 +- test/auto/database/tst_databaseutils.cpp | 68 +++++++++ test/auto/database/tst_databaseutils.pro | 11 ++ .../{tst_database => database}/tst_sqlite.cpp | 0 .../{tst_database => database}/tst_sqlite.pro | 0 15 files changed, 432 insertions(+), 20 deletions(-) create mode 100644 src/serversdk/databasetable.cpp create mode 100644 src/serversdk/databasetable.h create mode 100644 src/serversdk/databaseutils.cpp create mode 100644 src/serversdk/databaseutils.h rename test/auto/{tst_database/tst_databasetest.cpp => database/tst_databasemanager.cpp} (91%) create mode 100644 test/auto/database/tst_databasemanager.pro create mode 100644 test/auto/database/tst_databasetable.cpp rename test/auto/{tst_database/tst_database.pro => database/tst_databasetable.pro} (81%) create mode 100644 test/auto/database/tst_databaseutils.cpp create mode 100644 test/auto/database/tst_databaseutils.pro rename test/auto/{tst_database => database}/tst_sqlite.cpp (100%) rename test/auto/{tst_database => database}/tst_sqlite.pro (100%) diff --git a/serversdkint.pri b/serversdkint.pri index 4659012..bec8e78 100644 --- a/serversdkint.pri +++ b/serversdkint.pri @@ -26,6 +26,7 @@ else:win32:CONFIG(debug, debug|release): LIBS += -L$$SDKDIR/debug/ -lserversdk else:unix: LIBS += -L$$SDKDIR/ -lserversdk -lz QT += network sql +CONFIG += c++14 INCLUDEPATH += $$PWD/src/ DEPENDPATH += $$PWD/src/serversdk diff --git a/src/serversdk/databasetable.cpp b/src/serversdk/databasetable.cpp new file mode 100644 index 0000000..736a65d --- /dev/null +++ b/src/serversdk/databasetable.cpp @@ -0,0 +1,61 @@ +#include "databasetable.h" + +#include "logger.h" + +#include + +namespace serversdk { + +DatabaseTable::DatabaseTable(QObject *parent) : QObject(parent) +{ +} + + +/** Creates universal variant structure to hold key / value pairs */ +QVariantMap DatabaseTable::toVariantMap() +{ + QVariantMap variantMap; + + const QMetaObject *metaObj = this->metaObject(); + qDebug() << "properties: "; + for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { + QMetaProperty prop = metaObj->property(i); + // Skip non USER so -> ( Q_PROPERTY( ...... USER false ) only + if (!prop.isUser()) + continue; + + // Get key -> value pairs + QString name = prop.name(); + QVariant::Type type = prop.type(); + QVariant value(type); + value.setValue(prop.read(this)); + + variantMap.insert(name, value); + } + return variantMap; +} + +QString DatabaseTable::createTableSQL() const +{ + QString sql = QString("CREATE TABLE %1").arg(this->metaObject()->className()); + + QVariantMap variantMap; + + const QMetaObject *metaObj = this->metaObject(); + qDebug() << "properties: "; + for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { + QMetaProperty prop = metaObj->property(i); + // Skip non USER so -> ( Q_PROPERTY( ...... USER false ) only + if (!prop.isUser()) + continue; + + QString name = prop.name(); + + + } +} + + + + +} // namespace serversdk diff --git a/src/serversdk/databasetable.h b/src/serversdk/databasetable.h new file mode 100644 index 0000000..37bc652 --- /dev/null +++ b/src/serversdk/databasetable.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + + +// Default TABLE_PROPERTY +#define TABLE_PROPERTY(TYPE, NAME, SETNAME, INITVAL) \ + Q_PROPERTY(TYPE NAME READ NAME WRITE SETNAME NOTIFY NAME ## Changed USER true) \ + public: \ + TYPE NAME() const { return a_ ## NAME ; } \ + Q_SLOT void SETNAME(TYPE value) { \ + if (a_ ## NAME == value) return; \ + a_ ## NAME = value; \ + emit NAME ## Changed(value); \ + } \ + Q_SIGNAL void NAME ## Changed(TYPE value);\ + protected: \ + TYPE a_ ## NAME = INITVAL; + +namespace serversdk { + +class DatabaseTable : public QObject +{ + Q_OBJECT + TABLE_PROPERTY(int, id, setId, 0); + TABLE_PROPERTY(QString, name, setName, QString()); +public: + explicit DatabaseTable(QObject *parent = nullptr); + QVariantMap toVariantMap(); + QString createTableSQL() const; + + +signals: + +public slots: +}; + +} // namespace serversdk + diff --git a/src/serversdk/databaseutils.cpp b/src/serversdk/databaseutils.cpp new file mode 100644 index 0000000..0499e25 --- /dev/null +++ b/src/serversdk/databaseutils.cpp @@ -0,0 +1,134 @@ +#include "databaseutils.h" +#include "logger.h" + +#include + +namespace serversdk { + +/** + * @brief DatabaseUtils::createTableSQL + * @param table + * @return + * + * @code{sql} + * "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)" + * @endcode + * + * Default types: + * + * + * + */ +DatabaseUtils::DatabaseUtils(const QString type) + : mSqlType(type) +{ + +} + +QString DatabaseUtils::createTableSQL(const DatabaseTable *table, const QString tableName) const +{ + QString userData; + + if (tableName.isEmpty()) + return QString(); + + // out: "CREATE TABLE colnod_subject ( " + QString sql = QString("CREATE TABLE %1 ( ").arg(tableName); + + const QMetaObject *metaObj = table->metaObject(); + int total = metaObj->propertyCount(); + int first = true; + for (int i = metaObj->propertyOffset(); i < total; ++i) { + QMetaProperty prop = metaObj->property(i); + // Skip non USER so -> ( Q_PROPERTY( ...... USER false ) only + if (!prop.isUser()) + continue; + + QString name = prop.name(); + QVariant::Type type = prop.type(); + QString textType; + + + // Getting correct type based on QVariant + if (sqlType() == "SQLITE") { + textType = this->textSQLIteType(type); + } else if (sqlType() == "PSQL") { + qCDebug(sdkdb) << "Implemented only for SQLITE"; + return QString(); + + } else { + qCDebug(sdkdb) << "Implemented only for SQLITE"; + return QString(); + } + + // in: id TEXT + // out: "CREATE TABLE colnod_subject ( id TEXT " + sql += QString("%1 %2").arg(name,textType); + + // in: PRIMARY KEY + // out: "CREATE TABLE colnod_subject ( id TEXT PRIMARY KEY" + if (first) { // first row + sql += " PRIMARY KEY"; + first = false; + } + + // in: NOT NULL + // out: "CREATE TABLE colnod_subject ( id TEXT PRIMARY KEY NOT NULL" + if (!userData.isEmpty()) { + sql += " " + userData; + } + + // out: "CREATE TABLE colnod_subject ( id TEXT PRIMARY KEY NOT NULL," + if (i < (total-1)) { // second last + sql += ", "; + } + } + + // out: ".. subject_id TEXT)" + sql+= " )"; + + return sql; +} + +QString DatabaseUtils::sqlType() const +{ + return mSqlType; +} + +void DatabaseUtils::setSqlType(const QString &sqlType) +{ + mSqlType = sqlType; +} + +QString DatabaseUtils::textSQLIteType(QVariant::Type type) const +{ + switch (type) { + case QVariant::Int: + return QStringLiteral("INTEGER"); + break; + case QVariant::Uuid: + return QStringLiteral("TEXT"); + break; + case QVariant::String: + return QStringLiteral("TEXT"); + break; + case QVariant::DateTime: + return QStringLiteral("INTEGER"); + break; + case QVariant::Bool: + return QStringLiteral("INTEGER"); + break; + default: + return QString(); + break; + } +} + +} // namespace serversdk diff --git a/src/serversdk/databaseutils.h b/src/serversdk/databaseutils.h new file mode 100644 index 0000000..3fe6252 --- /dev/null +++ b/src/serversdk/databaseutils.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "databasetable.h" + +namespace serversdk { + +class DatabaseUtils : public QObject +{ + Q_OBJECT +public: + DatabaseUtils(const QString type = "SQLITE"); + QString createTableSQL(const DatabaseTable *table, const QString table_name) const; + + QString sqlType() const; + void setSqlType(const QString &sqlType); + +private: + QString textSQLIteType(QVariant::Type type) const; +private: + QString mSqlType; +}; + +} // namespace serversdk + diff --git a/src/serversdk/serversdk.pro b/src/serversdk/serversdk.pro index a55fa6b..432bb48 100644 --- a/src/serversdk/serversdk.pro +++ b/src/serversdk/serversdk.pro @@ -8,21 +8,25 @@ CONFIG += c++14 INCLUDEPATH += $$PWD SOURCES += \ - $$PWD/core.cpp \ - $$PWD/databasemanager.cpp \ - $$PWD/logger.cpp \ - $$PWD/loggermessage.cpp \ - $$PWD/requestmanager.cpp \ - $$PWD/request.cpp \ - $$PWD/utils.cpp \ - $$PWD/soaprequest.cpp + core.cpp \ + databasemanager.cpp \ + databaseutils.cpp \ + logger.cpp \ + loggermessage.cpp \ + requestmanager.cpp \ + request.cpp \ + utils.cpp \ + soaprequest.cpp \ + databasetable.cpp HEADERS += \ - $$PWD/core.h \ - $$PWD/databasemanager.h \ - $$PWD/logger.h \ - $$PWD/loggermessage.h \ - $$PWD/requestmanager.h \ - $$PWD/request.h \ - $$PWD/utils.h \ - $$PWD/soaprequest.h + core.h \ + databasemanager.h \ + databaseutils.h \ + logger.h \ + loggermessage.h \ + requestmanager.h \ + request.h \ + utils.h \ + soaprequest.h \ + databasetable.h diff --git a/test/auto/auto.pro b/test/auto/auto.pro index 858079a..2ce442a 100644 --- a/test/auto/auto.pro +++ b/test/auto/auto.pro @@ -3,6 +3,8 @@ TEMPLATE = subdirs SUBDIRS += \ tst_soap \ tst_request \ - tst_database/tst_database.pro \ - tst_database/tst_sqlite.pro \ + database/tst_databasemanager.pro \ + database/tst_sqlite.pro \ + database/tst_databasetable.pro \ + database/tst_databaseutils.pro \ tst_utils diff --git a/test/auto/tst_database/tst_databasetest.cpp b/test/auto/database/tst_databasemanager.cpp similarity index 91% rename from test/auto/tst_database/tst_databasetest.cpp rename to test/auto/database/tst_databasemanager.cpp index 4193e6a..38413b4 100644 --- a/test/auto/tst_database/tst_databasetest.cpp +++ b/test/auto/database/tst_databasemanager.cpp @@ -33,4 +33,4 @@ void DatabaseTest::createDb_test() QTEST_APPLESS_MAIN(DatabaseTest) -#include "tst_databasetest.moc" +#include "tst_databasemanager.moc" diff --git a/test/auto/database/tst_databasemanager.pro b/test/auto/database/tst_databasemanager.pro new file mode 100644 index 0000000..40885ae --- /dev/null +++ b/test/auto/database/tst_databasemanager.pro @@ -0,0 +1,11 @@ +QT += testlib +QT -= gui + +CONFIG += qt console warn_on depend_includepath testcase +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += tst_databasemanager.cpp + +include(../../test.pri) diff --git a/test/auto/database/tst_databasetable.cpp b/test/auto/database/tst_databasetable.cpp new file mode 100644 index 0000000..cf19c3f --- /dev/null +++ b/test/auto/database/tst_databasetable.cpp @@ -0,0 +1,54 @@ +#include "serversdk/databasetable.h" + +#include + + +class BookTable : public serversdk::DatabaseTable +{ + Q_OBJECT + TABLE_PROPERTY(QUuid, uuid, setUuid, QUuid()); + TABLE_PROPERTY(int, id, setId, 0); + TABLE_PROPERTY(QString, name, setName, "Test"); +}; + + + +class DatabaseTableTest : public QObject +{ + Q_OBJECT + +public: + DatabaseTableTest(); + ~DatabaseTableTest(); + +private slots: + void constructor_test(); + void qvariant_test(); + +}; + +DatabaseTableTest::DatabaseTableTest() +{ +} + +DatabaseTableTest::~DatabaseTableTest() +{ +} + +void DatabaseTableTest::constructor_test() +{ + auto table = std::make_unique(); + QCOMPARE(table->id(), 0); + QCOMPARE(table->name(), QStringLiteral("Test")); +} + +void DatabaseTableTest::qvariant_test() +{ + auto table = std::make_unique(); + QVariantMap map = table->toVariantMap(); + QCOMPARE(map.count(), 3); +} + +QTEST_APPLESS_MAIN(DatabaseTableTest) + +#include "tst_databasetable.moc" diff --git a/test/auto/tst_database/tst_database.pro b/test/auto/database/tst_databasetable.pro similarity index 81% rename from test/auto/tst_database/tst_database.pro rename to test/auto/database/tst_databasetable.pro index 49cc8db..5332177 100644 --- a/test/auto/tst_database/tst_database.pro +++ b/test/auto/database/tst_databasetable.pro @@ -6,6 +6,6 @@ CONFIG -= app_bundle TEMPLATE = app -SOURCES += tst_databasetest.cpp +SOURCES += tst_databasetable.cpp include(../../test.pri) diff --git a/test/auto/database/tst_databaseutils.cpp b/test/auto/database/tst_databaseutils.cpp new file mode 100644 index 0000000..f94b59e --- /dev/null +++ b/test/auto/database/tst_databaseutils.cpp @@ -0,0 +1,68 @@ +#include "serversdk/databaseutils.h" +#include "serversdk/databasetable.h" + +#include + + +class BookTable : public serversdk::DatabaseTable +{ + Q_OBJECT + Q_CLASSINFO("default_table", "books") + TABLE_PROPERTY(QUuid, id, set_id, QUuid()); + TABLE_PROPERTY(QString, description, set_description, ""); + TABLE_PROPERTY(QDateTime, last_update, set_last_update, QDateTime::fromSecsSinceEpoch(0)); + TABLE_PROPERTY(QString, name, set_name, QString()); + TABLE_PROPERTY(QString, pin, set_pin, QString()); + TABLE_PROPERTY(QDateTime, last_modified, set_last_modified, QDateTime::fromSecsSinceEpoch(0)); + TABLE_PROPERTY(bool, deleted, set_deleted, false); +}; + + + +class DatabaseUtilsTest : public QObject +{ + Q_OBJECT + +public: + DatabaseUtilsTest(); + ~DatabaseUtilsTest(); + QString uuid() const; + void setUuid(QString value); + +private slots: + void createTable_test(); + +}; + +DatabaseUtilsTest::DatabaseUtilsTest() +{ +} + +DatabaseUtilsTest::~DatabaseUtilsTest() +{ +} + +void DatabaseUtilsTest::createTable_test() +{ + auto table = std::make_unique(); + + serversdk::DatabaseUtils utils; + QString sql = utils.createTableSQL(table.get(), "subjects"); + + QString expected = "CREATE TABLE subjects ( " + "id TEXT PRIMARY KEY, " + "description TEXT, " + "last_update INTEGER, " + "name TEXT, " + "pin TEXT, " + "last_modified INTEGER, " + "deleted INTEGER )"; + + QCOMPARE(sql, expected); +} + + + +QTEST_APPLESS_MAIN(DatabaseUtilsTest) + +#include "tst_databaseutils.moc" diff --git a/test/auto/database/tst_databaseutils.pro b/test/auto/database/tst_databaseutils.pro new file mode 100644 index 0000000..bbda891 --- /dev/null +++ b/test/auto/database/tst_databaseutils.pro @@ -0,0 +1,11 @@ +QT += testlib +QT -= gui + +CONFIG += qt console warn_on depend_includepath testcase +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += tst_databaseutils.cpp + +include(../../test.pri) diff --git a/test/auto/tst_database/tst_sqlite.cpp b/test/auto/database/tst_sqlite.cpp similarity index 100% rename from test/auto/tst_database/tst_sqlite.cpp rename to test/auto/database/tst_sqlite.cpp diff --git a/test/auto/tst_database/tst_sqlite.pro b/test/auto/database/tst_sqlite.pro similarity index 100% rename from test/auto/tst_database/tst_sqlite.pro rename to test/auto/database/tst_sqlite.pro