From ac61c26d561382f5b76ab3359e5f2a702cbd2a47 Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Sat, 20 Jul 2019 23:33:17 +0200 Subject: [PATCH] add: basic relay structure add: improved sql calls --- controllers/api_v1_devices.cc | 18 ++- controllers/api_v1_devices.h | 26 ++-- controllers/api_v1_devices_relays.cc | 57 ++++++++ controllers/api_v1_schedules.cc | 41 +++--- helpers.h | 12 ++ helpers/create_sql_filtered_query.cc | 49 +++++++ models/device_dbo.cc | 24 ++-- models/device_dbo.h | 4 +- models/relay_dbo.cc | 190 +++++++++++++++++++++++++++ models/relay_dbo.h | 41 ++++++ models/schedule_dbo.cc | 37 +++--- models/schedule_dbo.h | 4 +- 12 files changed, 426 insertions(+), 77 deletions(-) create mode 100644 controllers/api_v1_devices_relays.cc create mode 100644 helpers/create_sql_filtered_query.cc create mode 100644 models/relay_dbo.cc create mode 100644 models/relay_dbo.h diff --git a/controllers/api_v1_devices.cc b/controllers/api_v1_devices.cc index c9a193f..d8aaa4f 100644 --- a/controllers/api_v1_devices.cc +++ b/controllers/api_v1_devices.cc @@ -24,15 +24,13 @@ devices::get_all(const HttpRequestPtr &req, std::function &&callback, const std::string& device_id) { - device_dbo *device = device_dbo::get_one_by("id", device_id.c_str()); + device_dbo **devices = device_dbo::get_by_simple("id", device_id.c_str(), (intptr_t) &sqlite3_bind_text); - if(device) + if(devices[0]) { - auto resp = HttpResponse::newHttpJsonResponse(device->to_json()); + auto resp = HttpResponse::newHttpJsonResponse(devices[0]->to_json()); callback(resp); - - free(device); } else { @@ -41,24 +39,23 @@ devices::get_one_by_id(const HttpRequestPtr &req, std::function &&callback, const std::string& device_id) { - device_dbo *device = device_dbo::get_one_by("id", device_id.c_str()); + device_dbo **devices = device_dbo::get_by_simple("id", device_id.c_str(), (intptr_t) &sqlite3_bind_text); - if(device) + if(devices[0]) { auto resp = HttpResponse::newHttpResponse(); - if(!device->remove()) + if(!devices[0]->remove()) { resp->setStatusCode(k500InternalServerError); } callback(resp); - - free(device); } else { @@ -67,4 +64,5 @@ devices::delete_one_by_id(const HttpRequestPtr &req, std::function &&callback); - static void get_all(const HttpRequestPtr& req,std::function &&callback); - static void get_one_by_id(const HttpRequestPtr& req,std::function &&callback,const std::string& device_id); - static void delete_one_by_id(const HttpRequestPtr& req,std::function &&callback,const std::string& device_id); - //void get_relays_all(const HttpRequestPtr& req,std::function &&callback,std::string device_id); - //void get_relays_one(const HttpRequestPtr& req,std::function &&callback,std::string device_id,std::string relay_id); + static void post_discover(const HttpRequestPtr& req, std::function &&callback); + static void get_all(const HttpRequestPtr& req, std::function &&callback); + static void get_one_by_id(const HttpRequestPtr& req, std::function &&callback, const std::string& device_id); + static void delete_one_by_id(const HttpRequestPtr& req, std::function &&callback, const std::string& device_id); + + static void get_relays_all(const HttpRequestPtr& req, std::function &&callback, const std::string& device_id); + static void get_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function &&callback, const std::string& device_id, int relay_num); + static void put_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function &&callback, const std::string& device_id, int relay_num); }; } } diff --git a/controllers/api_v1_devices_relays.cc b/controllers/api_v1_devices_relays.cc new file mode 100644 index 0000000..7c5ba80 --- /dev/null +++ b/controllers/api_v1_devices_relays.cc @@ -0,0 +1,57 @@ +#include +#include +#include "api_v1_devices.h" + +using namespace api::v1; + +void +devices::get_relays_all(const HttpRequestPtr &req, std::function &&callback, + const std::string& device_id) +{ + + relay_dbo **all_device_relays = relay_dbo::get_by_simple("device_id", (void *) device_id.c_str(), + (intptr_t) sqlite3_bind_text); + Json::Value all_relays_json(Json::arrayValue); + + for(int i = 0; all_device_relays[i] != nullptr; i++) + { + all_relays_json.append(all_device_relays[i]->to_json()); + } + + auto resp = HttpResponse::newHttpJsonResponse(all_relays_json); + + callback(resp); + + relay_dbo::free_list(all_device_relays); +} + +void +devices::get_relays_one_by_id_and_num(const HttpRequestPtr &req, + std::function &&callback, const std::string& device_id, + int relay_num) +{ + relay_dbo **relays = relay_dbo::get_by_simple("id", (void *) (intptr_t) relay_num, (intptr_t) &sqlite3_bind_int); + + if(relays[0]) + { + auto resp = HttpResponse::newHttpJsonResponse(relays[0]->to_json()); + + callback(resp); + } + else + { + auto resp = HttpResponse::newHttpResponse(); + resp->setStatusCode(k404NotFound); + + callback(resp); + } + relay_dbo::free_list(relays); +} + +void +devices::put_relays_one_by_id_and_num(const HttpRequestPtr &req, + std::function &&callback, const std::string& device_id, + int relay_num) +{ + +} \ No newline at end of file diff --git a/controllers/api_v1_schedules.cc b/controllers/api_v1_schedules.cc index dea2e17..613f4bf 100644 --- a/controllers/api_v1_schedules.cc +++ b/controllers/api_v1_schedules.cc @@ -27,15 +27,13 @@ schedules::get_all(const HttpRequestPtr &req, std::function &&callback, const std::string& schedule_id) { - schedule_dbo *schedule = schedule_dbo::get_one_by("id", schedule_id.c_str()); + schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text); - if(schedule) + if(schedules[0]) { - auto resp = HttpResponse::newHttpJsonResponse(schedule->to_json()); + auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json()); callback(resp); - - delete schedule; } else { @@ -44,24 +42,23 @@ schedules::get_one_by_id(const HttpRequestPtr &req, std::function &&callback, const std::string& schedule_id) { - schedule_dbo *schedule = schedule_dbo::get_one_by("id", schedule_id.c_str()); + schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text); - if(schedule) + if(schedules[0]) { auto resp = HttpResponse::newHttpResponse(); - if(!schedule->remove()) + if(!schedules[0]->remove()) { resp->setStatusCode(k500InternalServerError); } callback(resp); - - delete schedule; } else { @@ -70,6 +67,7 @@ schedules::delete_one_by_id(const HttpRequestPtr &req, std::function &&callback, const std::string &schedule_id) +schedules::put_one_by_id(const HttpRequestPtr &req, std::function &&callback, + const std::string &schedule_id) { Json::Value body = *req->jsonObject(); - schedule_dbo *schedule = schedule_dbo::get_one_by("id", schedule_id.c_str()); + schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text); - if(schedule) + if(schedules[0]) { - strncpy(schedule->name, body["name"].asCString(), 127); - schedule->name[127] = '\0'; - delete schedule->periods; - schedule->periods = helpers::parse_periods(body["periods"]); + strncpy(schedules[0]->name, body["name"].asCString(), 127); + schedules[0]->name[127] = '\0'; + delete schedules[0]->periods; + schedules[0]->periods = helpers::parse_periods(body["periods"]); - if(!schedule->update()) + if(!schedules[0]->update()) { auto resp = HttpResponse::newHttpResponse(); resp->setStatusCode(k500InternalServerError); @@ -120,11 +119,9 @@ schedules::put_one_by_id(const HttpRequestPtr &req, std::functionto_json()); + auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json()); callback(resp); } - - delete schedule; } else { @@ -133,5 +130,5 @@ schedules::put_one_by_id(const HttpRequestPtr &req, std::function #include #include +#include namespace helpers { @@ -18,6 +19,17 @@ namespace helpers period_list* parse_periods(Json::Value periods_json); + + typedef struct s_sql_filter_builder + { + const char *col_name; + const void *value; + const intptr_t bind_func; + const char *logic; + } sql_filter_builder; + + sqlite3_stmt* + create_sql_filtered_query(const char *sql, sql_filter_builder **filters); } #endif //EMGAUWA_CORE_HELPERS_H diff --git a/helpers/create_sql_filtered_query.cc b/helpers/create_sql_filtered_query.cc new file mode 100644 index 0000000..f55caa7 --- /dev/null +++ b/helpers/create_sql_filtered_query.cc @@ -0,0 +1,49 @@ +#include +#include +#include + +sqlite3_stmt* +helpers::create_sql_filtered_query(const char *sql, sql_filter_builder **filters) +{ + char *old_query = (char*)sql; + char *new_query; + + sql_filter_builder *filter; + int filter_count = 0; + + do + { + filter = filters[filter_count]; + + filter_count++; + asprintf(&new_query, " %s %s=?%d %s", old_query, filter->col_name, filter_count, filter->logic); + + if(old_query != sql) + { + free(old_query); + } + old_query = new_query; + } while(filter->logic[0] != ';'); + + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(globals::db, new_query, -1, &stmt, nullptr); + + free(new_query); + + for(int i = 0; i < filter_count; i++) + { + filter = filters[i]; + + if(filter->bind_func == (intptr_t)&sqlite3_bind_int) + { + sqlite3_bind_int(stmt, i + 1, (int)(intptr_t)filter->value); + } + if(filter->bind_func == (intptr_t)&sqlite3_bind_text) + { + sqlite3_bind_text(stmt, i + 1, (char*)filter->value, -1, SQLITE_STATIC); + } + } + + return stmt; +} \ No newline at end of file diff --git a/models/device_dbo.cc b/models/device_dbo.cc index c80df82..43f8b13 100644 --- a/models/device_dbo.cc +++ b/models/device_dbo.cc @@ -2,6 +2,7 @@ #include #include #include +#include #include "device_dbo.h" #include "globals.h" @@ -169,18 +170,21 @@ device_dbo::get_all() return device_db_select(stmt); } -device_dbo* -device_dbo::get_one_by(const char *key, const char *value) +device_dbo** +device_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func) { - sqlite3_stmt *stmt; - char* sql; + helpers::sql_filter_builder *filters[1]; + helpers::sql_filter_builder filter + { + key, + value, + bind_func, + ";" + }; + filters[0] = &filter; + sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM devices WHERE", filters); - asprintf(&sql, "SELECT * FROM devices WHERE %s=?1;", key); - - sqlite3_prepare_v2(globals::db, sql, -1, &stmt, nullptr); - sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC); - - return device_db_select(stmt)[0]; + return device_db_select(stmt); } void diff --git a/models/device_dbo.h b/models/device_dbo.h index 8fb945a..9ed4133 100644 --- a/models/device_dbo.h +++ b/models/device_dbo.h @@ -31,8 +31,8 @@ public: static void free_list(device_dbo **devices_list); - static device_dbo* - get_one_by(const char *key, const char *value); + static device_dbo** + get_by_simple(const char *key, const void *value, intptr_t bind_func); static device_dbo** get_all(); diff --git a/models/relay_dbo.cc b/models/relay_dbo.cc new file mode 100644 index 0000000..b523f7f --- /dev/null +++ b/models/relay_dbo.cc @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include "relay_dbo.h" +#include "globals.h" + +static bool relay_db_update_insert(relay_dbo *relay, sqlite3_stmt *stmt) +{ + int rc; + + sqlite3_bind_int(stmt, 1, relay->id); + sqlite3_bind_int(stmt, 2, relay->number); + sqlite3_bind_text(stmt, 3, relay->name, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 4, relay->active_schedule_id, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 5, relay->device_id, -1, SQLITE_STATIC); + + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) + { + printf("ERROR inserting data: %s\n", sqlite3_errmsg(globals::db)); + return false; + } + + sqlite3_finalize(stmt); + + return true; +} + +static relay_dbo* +relay_db_select_mapper(sqlite3_stmt *stmt) +{ + auto *new_relay = (relay_dbo*)malloc(sizeof(relay_dbo)); + for(int i = 0; i < sqlite3_column_count(stmt); i++) + { + const char *name = sqlite3_column_name(stmt, i); + switch(name[0]) + { + case 'a': // active_schedule_id + strncpy(new_relay->active_schedule_id, (const char*)sqlite3_column_text(stmt, i), 33); + break; + case 'd': // device_id + strncpy(new_relay->device_id, (const char*)sqlite3_column_text(stmt, i), 33); + break; + case 'i': + new_relay->id = sqlite3_column_int(stmt, i); + break; + case 'n': + switch(name[0]) + { + case 'a': // name + strncpy(new_relay->name, (const char*)sqlite3_column_text(stmt, i), 127); + break; + case 'u': // number + new_relay->number = sqlite3_column_int(stmt, i); + break; + default: + break; + } + default: // ignore columns not implemented + break; + } + } + return new_relay; +} + +static relay_dbo** +relay_db_select(sqlite3_stmt *stmt) +{ + auto **all_relays = (relay_dbo**)malloc(sizeof(relay_dbo*)); + + int row = 0; + + while(true) + { + int s; + + s = sqlite3_step(stmt); + if (s == SQLITE_ROW) + { + relay_dbo *new_relay = relay_db_select_mapper(stmt); + row++; + + all_relays = (relay_dbo**)realloc(all_relays, sizeof(relay_dbo*) * (row + 1)); + all_relays[row - 1] = new_relay; + } + else + { + if (s == SQLITE_DONE) + { + break; + } + else + { + LOG_ERROR << "Error Selecting relays from database"; + sqlite3_finalize(stmt); + return nullptr; + } + } + } + + sqlite3_finalize(stmt); + all_relays[row] = nullptr; + + return all_relays; +} + +bool +relay_dbo::update() +{ + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(globals::db, "UPDATE relays set number = ?2, name = ?3, active_schedule_id = ?4, device_id = ?5 WHERE id = ?1;", -1, &stmt, nullptr); + + return relay_db_update_insert(this, stmt); +} + +bool +relay_dbo::insert() +{ + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(globals::db, "INSERT INTO relays(id, number, name, active_schedule_id, device_id) values (?1, ?2, ?3, ?4, ?5);", -1, &stmt, nullptr); + + return relay_db_update_insert(this, stmt); +} + +bool +relay_dbo::remove() +{ + sqlite3_stmt *stmt; + int rc; + + sqlite3_prepare_v2(globals::db, "DELETE FROM relays WHERE id=?1;", -1, &stmt, nullptr); + sqlite3_bind_int(stmt, 1, this->id); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + return rc == SQLITE_DONE; + +} + +Json::Value +relay_dbo::to_json() +{ + Json::Value relay_json; + relay_json["name"] = this->name; + relay_json["id"] = this->id; + + return relay_json; +} + +relay_dbo** +relay_dbo::get_all() +{ + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(globals::db, "SELECT * FROM relays;", -1, &stmt, nullptr); + + return relay_db_select(stmt); +} + +relay_dbo** +relay_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func) +{ + helpers::sql_filter_builder *filters[1]; + helpers::sql_filter_builder filter + { + key, + value, + bind_func, + ";" + }; + filters[0] = &filter; + sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM relays WHERE", filters); + + return relay_db_select(stmt); +} + +void +relay_dbo::free_list(relay_dbo **relays_list) +{ + for(int i = 0; relays_list[i] != nullptr; i++) + { + free(relays_list[i]); + } + free(relays_list); +} + diff --git a/models/relay_dbo.h b/models/relay_dbo.h new file mode 100644 index 0000000..1c99b67 --- /dev/null +++ b/models/relay_dbo.h @@ -0,0 +1,41 @@ +#ifndef EMGAUWA_CORE_RELAY_DBO_H +#define EMGAUWA_CORE_RELAY_DBO_H + +#include +#include +#include + +class relay_dbo +{ +public: + + int id; + char name[128]; + int number; + char device_id[33]; + char active_schedule_id[33]; + + bool + update(); + + bool + insert(); + + bool + remove(); + + Json::Value + to_json(); + + static void + free_list(relay_dbo **relays_list); + + static relay_dbo** + get_by_simple(const char *key, const void *value, intptr_t bind_func); + + static relay_dbo** + get_all(); +}; + + +#endif //EMGAUWA_CORE_RELAY_DBO_H diff --git a/models/schedule_dbo.cc b/models/schedule_dbo.cc index 4327709..1917f3f 100644 --- a/models/schedule_dbo.cc +++ b/models/schedule_dbo.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include "schedule_dbo.h" #include "globals.h" #include "period.h" @@ -83,15 +84,14 @@ schedule_db_select(sqlite3_stmt *stmt) } else { - if (s == SQLITE_DONE) + if(s == SQLITE_DONE) { break; } else { - LOG_ERROR << "Error Selecting schedules from database"; - sqlite3_finalize(stmt); - return nullptr; + LOG_ERROR << "Error Selecting schedules from database: " << sqlite3_errstr(s); + break; } } } @@ -165,24 +165,21 @@ schedule_dbo::get_all() return schedule_db_select(stmt); } -schedule_dbo* -schedule_dbo::get_one_by(const char *key, const char *value) +schedule_dbo** +schedule_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func) { - sqlite3_stmt *stmt; - char* sql; + helpers::sql_filter_builder *filters[1]; + helpers::sql_filter_builder filter + { + key, + value, + bind_func, + ";" + }; + filters[0] = &filter; + sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM schedules WHERE", filters); - asprintf(&sql, "SELECT * FROM schedules WHERE %s=?1;", key); - - sqlite3_prepare_v2(globals::db, sql, -1, &stmt, nullptr); - sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC); - - schedule_dbo **schedule_list = schedule_db_select(stmt); - schedule_dbo *target_schedule = schedule_list[0]; - - free(schedule_list); - free(sql); - - return target_schedule; + return schedule_db_select(stmt); } void diff --git a/models/schedule_dbo.h b/models/schedule_dbo.h index 4adb78f..1a6a886 100644 --- a/models/schedule_dbo.h +++ b/models/schedule_dbo.h @@ -32,8 +32,8 @@ public: static void free_list(schedule_dbo **schedules_list); - static schedule_dbo* - get_one_by(const char *key, const char *value); + static schedule_dbo** + get_by_simple(const char *key, const void *value, intptr_t bind_func); static schedule_dbo** get_all();