From b35183d761323b3bf7685bd18a1d45e0a7dc040f Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 29 Feb 2024 15:18:04 +0100 Subject: [PATCH] SHV API: Add SQL node --- quickevent/app/quickevent/CMakeLists.txt | 2 + .../Event/src/services/shvapi/client.cpp | 4 +- .../Event/src/services/shvapi/nodes.cpp | 4 +- .../plugins/Event/src/services/shvapi/nodes.h | 6 +- .../src/services/shvapi/rpcsqlresult.cpp | 183 ++++++++++++++++++ .../Event/src/services/shvapi/rpcsqlresult.h | 50 +++++ .../Event/src/services/shvapi/sqlnode.cpp | 49 +++++ .../Event/src/services/shvapi/sqlnode.h | 20 ++ 8 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.cpp create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.h create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.cpp create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.h diff --git a/quickevent/app/quickevent/CMakeLists.txt b/quickevent/app/quickevent/CMakeLists.txt index 67989b413..36e995f6c 100644 --- a/quickevent/app/quickevent/CMakeLists.txt +++ b/quickevent/app/quickevent/CMakeLists.txt @@ -130,7 +130,9 @@ if(WITH_QE_SHVAPI) plugins/Event/src/services/shvapi/client.cpp plugins/Event/src/services/shvapi/clientwidget.cpp plugins/Event/src/services/shvapi/shvnode.cpp + plugins/Event/src/services/shvapi/sqlnode.cpp plugins/Event/src/services/shvapi/nodes.cpp + plugins/Event/src/services/shvapi/rpcsqlresult.cpp ) endif() diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/client.cpp b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/client.cpp index c929c5f50..c4173d1ba 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/client.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/client.cpp @@ -1,6 +1,7 @@ #include "client.h" #include "clientwidget.h" #include "nodes.h" +#include "sqlnode.h" #include "../../eventplugin.h" @@ -56,8 +57,9 @@ Client::Client(QObject *parent) auto *stage = new shv::iotqt::node::ShvNode("stage", event); for (auto i = 0; i < event_plugin->stageCount(); i++) { auto *nd = new shv::iotqt::node::ShvNode(std::to_string(i + 1), stage); - new RunNode(i, nd); + new StartListStarterNode(i, nd); } + new SqlNode(m_rootNode); } else { qDeleteAll(m_rootNode->findChildren()); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.cpp b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.cpp index cbdcd33a8..fa7d519e0 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.cpp @@ -79,7 +79,7 @@ static auto METH_RECORD = "record"; static auto METH_SET_RECORD = "setRecord"; static auto SIG_REC_CHNG = "recchng"; -const std::vector &RunNode::metaMethods() +const std::vector &StartListStarterNode::metaMethods() { static std::vector meta_methods { {Rpc::METH_DIR, MetaMethod::Signature::RetParam, MetaMethod::Flag::None, Rpc::ROLE_BROWSE}, @@ -92,7 +92,7 @@ const std::vector &RunNode::metaMethods() return meta_methods; } -RpcValue RunNode::callMethod(const StringViewList &shv_path, const std::string &method, const shv::chainpack::RpcValue ¶ms, const shv::chainpack::RpcValue &user_id) +RpcValue StartListStarterNode::callMethod(const StringViewList &shv_path, const std::string &method, const shv::chainpack::RpcValue ¶ms, const shv::chainpack::RpcValue &user_id) { qfLogFuncFrame() << shv_path.join('/') << method; //eyascore::utils::UserId user_id = eyascore::utils::UserId::makeUserName(QString::fromStdString(rq.userId().toMap().value("userName").toString())); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.h b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.h index 45d39cd26..5bdd103e0 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/nodes.h @@ -29,14 +29,14 @@ class EventNode : public shvapi::ShvNode shv::chainpack::RpcValue callMethod(const StringViewList &shv_path, const std::string &method, const shv::chainpack::RpcValue ¶ms, const shv::chainpack::RpcValue &user_id) override; }; -class RunNode : public shvapi::ShvNode +class StartListStarterNode : public shvapi::ShvNode { Q_OBJECT using Super = shvapi::ShvNode; public: - explicit RunNode(int stage, shv::iotqt::node::ShvNode *parent) - : Super("startlist", parent) + explicit StartListStarterNode(int stage, shv::iotqt::node::ShvNode *parent) + : Super("startliststarter", parent) , m_stage(stage) {} private: diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.cpp b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.cpp new file mode 100644 index 000000000..3594c28c6 --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.cpp @@ -0,0 +1,183 @@ +#include "rpcsqlresult.h" + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace shv::chainpack; + +namespace Event::services::shvapi { + +RpcValue RpcSqlField::toRpcValue() const +{ + RpcValue::Map ret; + ret["name"] = name.toStdString(); + ret["type"] = type; + ret["typeName"] = QMetaType(type).name(); + return RpcValue(std::move(ret)); +} + +QVariant RpcSqlField::toVariant() const +{ + QVariantMap ret; + ret["name"] = name; + ret["type"] = type; + ret["typeName"] = QMetaType(type).name(); + return ret; +} + +RpcSqlField RpcSqlField::fromRpcValue(const shv::chainpack::RpcValue &rv) +{ + RpcSqlField ret; + const RpcValue::Map &map = rv.asMap(); + ret.name = QString::fromStdString(map.value("name").asString()); + ret.type = map.value("type").toInt(); + return ret; +} + +RpcSqlField RpcSqlField::fromVariant(const QVariant &v) +{ + RpcSqlField ret; + const QVariantMap map = v.toMap(); + ret.name = map.value("name").toString(); + ret.type = map.value("type").toInt(); + return ret; +} + +RpcSqlResult::RpcSqlResult(const shv::chainpack::RpcResponse &resp) +{ + if(resp.isSuccess()) { + const RpcValue::Map result = resp.result().asMap(); + for(const RpcValue &rv : result.valref("fields").asList()) { + RpcSqlField fld; + fld.name = QString::fromStdString(rv.asMap().value("name").toString()); + fld.type = rv.asMap().value("type").toInt(); + fields.append(fld); + } + for(const RpcValue &rowv : result.valref("rows").asList()) { + Row row; + for(const RpcValue &rv : rowv.asList()) { + bool ok; + QVariant v = shv::coreqt::rpc::rpcValueToQVariant(rv, &ok); + if (!ok) { + qfError() << "Cannot convert:" << rv.toCpon() << "to QVariant"; + } + row.append(v); + } + rows.insert(rows.count(), row); + } + numRowsAffected = result.value("numRowsAffected").toInt(); + lastInsertId = result.value("lastInserId").toInt(); + } + else { + lastError = QString::fromStdString(resp.errorString()); + } +} + +QVariant RpcSqlResult::value(int col, int row) const +{ + return rows.value(row).toList().value(col); +} + +RpcValue RpcSqlResult::toRpcValue() const +{ + return shv::coreqt::rpc::qVariantToRpcValue(toVariant()); +} + +QVariant RpcSqlResult::toVariant() const +{ + QVariantMap ret; + if(isSelect()) { + QVariantList flds; + for(const auto &fld : this->fields) + flds.push_back(fld.toVariant()); + ret["fields"] = flds; + ret["rows"] = rows; + } + else { + ret["numRowsAffected"] = numRowsAffected; + ret["lastInsertId"] = lastInsertId; + } + return ret; +} + +/* +RpcSqlResult RpcSqlResult::fromRpcValue(const shv::chainpack::RpcValue &rv) +{ + RpcSqlResult ret; + const RpcValue::Map &map = rv.asMap(); + const RpcValue::List &flds = map.value("fields").asList(); + if(flds.empty()) { + ret.numRowsAffected = map.value("numRowsAffected").toInt(); + ret.lastInsertId = map.value("lastInsertId").toInt(); + } + else { + + } + return ret; + +} +*/ + +RpcSqlResult RpcSqlResult::fromVariant(const QVariant &v) +{ + RpcSqlResult ret; + const QVariantMap map = v.toMap(); + const QVariantList flds = map.value("fields").toList(); + if(flds.isEmpty()) { + ret.numRowsAffected = map.value("numRowsAffected").toInt(); + ret.lastInsertId = map.value("lastInsertId").toInt(); + } + else { + for(const QVariant &fv : flds) + ret.fields.append(RpcSqlField::fromVariant(fv)); + ret.rows = map.value("rows").toList(); + } + return ret; +} + +RpcSqlResult RpcSqlResult::fromRpcValue(const shv::chainpack::RpcValue &rv) +{ + auto v = shv::coreqt::rpc::rpcValueToQVariant(rv); + return fromVariant(v); +} + +RpcSqlResult RpcSqlResult::fromQuery(QSqlQuery &q) +{ + RpcSqlResult ret; + if(q.isSelect()) { + QSqlRecord rec = q.record(); + for (int i = 0; i < rec.count(); ++i) { + QSqlField fld = rec.field(i); + RpcSqlField rfld; + rfld.name = fld.name(); + rfld.type = fld.metaType().id(); + ret.fields.append(rfld); + } + while(q.next()) { + RpcSqlResult::Row row; + for (int i = 0; i < rec.count(); ++i) { + const QVariant v = q.value(i); + if (v.isNull()) + row.append(QVariant()); + else + row.append(v); + //shvError() << v << v.isNull() << jsv.toVariant() << jsv.toVariant().isNull(); + } + ret.rows.insert(ret.rows.count(), row); + } + } + else { + ret.numRowsAffected = q.numRowsAffected(); + ret.lastInsertId = q.lastInsertId().toInt(); + } + return ret; +} + +} diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.h b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.h new file mode 100644 index 000000000..f17d0b3ee --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/rpcsqlresult.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +namespace shv::chainpack { class RpcResponse; class RpcValue; } + +class QSqlQuery; + +namespace Event::services::shvapi { + +class RpcSqlField +{ +public: + QString name; + int type = 0; +public: + //explicit RpcSqlField(const QJsonObject &jo = QJsonObject()) : Super(jo) {} + shv::chainpack::RpcValue toRpcValue() const; + QVariant toVariant() const; + static RpcSqlField fromRpcValue(const shv::chainpack::RpcValue &rv); + static RpcSqlField fromVariant(const QVariant &v); +}; + +class RpcSqlResult +{ +public: + int numRowsAffected = 0; + int lastInsertId = 0; + QString lastError; + QVector fields; + using Row = QVariantList; + QVariantList rows; +public: + explicit RpcSqlResult() = default; + explicit RpcSqlResult(const shv::chainpack::RpcResponse &resp); + + QVariant value(int col, int row) const; + + bool isSelect() const {return !fields.isEmpty();} + shv::chainpack::RpcValue toRpcValue() const; + QVariant toVariant() const; + //static RpcSqlResult fromRpcValue(const shv::chainpack::RpcValue &rv); + static RpcSqlResult fromVariant(const QVariant &v); + static RpcSqlResult fromRpcValue(const shv::chainpack::RpcValue &rv); + static RpcSqlResult fromQuery(QSqlQuery &q); + +}; + +} diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.cpp b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.cpp new file mode 100644 index 000000000..4e4b53d6c --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.cpp @@ -0,0 +1,49 @@ +#include "sqlnode.h" +#include "rpcsqlresult.h" + +#include +#include +#include + +#include +#include + +using namespace shv::chainpack; + +namespace Event::services::shvapi { + +SqlNode::SqlNode(shv::iotqt::node::ShvNode *parent) + : Super("sql", parent) +{ + +} + +static auto METH_EXEC_SQL = "execSql"; + +const std::vector &SqlNode::metaMethods() +{ + static std::vector meta_methods { + {Rpc::METH_DIR, MetaMethod::Signature::RetParam, MetaMethod::Flag::None, Rpc::ROLE_BROWSE}, + {Rpc::METH_LS, MetaMethod::Signature::RetParam, MetaMethod::Flag::None, Rpc::ROLE_BROWSE}, + {METH_EXEC_SQL, MetaMethod::Signature::RetVoid, MetaMethod::Flag::None, Rpc::ROLE_WRITE}, + }; + return meta_methods; +} + +RpcValue SqlNode::callMethod(const StringViewList &shv_path, const std::string &method, const shv::chainpack::RpcValue ¶ms, const shv::chainpack::RpcValue &user_id) +{ + qfLogFuncFrame() << shv_path.join('/') << method; + //eyascore::utils::UserId user_id = eyascore::utils::UserId::makeUserName(QString::fromStdString(rq.userId().toMap().value("userName").toString())); + if(shv_path.empty()) { + if(method == METH_EXEC_SQL) { + qf::core::sql::Query q; + QString qs = params.to(); + q.exec(qs, qf::core::Exception::Throw); + auto res = RpcSqlResult::fromQuery(q); + return res.toRpcValue(); + } + } + return Super::callMethod(shv_path, method, params, user_id); +} + +} diff --git a/quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.h b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.h new file mode 100644 index 000000000..8a98b556b --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/shvapi/sqlnode.h @@ -0,0 +1,20 @@ +#pragma once + +#include "shvnode.h" + +namespace Event::services::shvapi { + +class SqlNode : public ShvNode +{ + Q_OBJECT + + using Super = ShvNode; +public: + explicit SqlNode(shv::iotqt::node::ShvNode *parent); + +protected: + const std::vector &metaMethods() override; + shv::chainpack::RpcValue callMethod(const StringViewList &shv_path, const std::string &method, const shv::chainpack::RpcValue ¶ms, const shv::chainpack::RpcValue &user_id) override; +}; + +}