MantisBase v0.3.4
Loading...
Searching...
No Matches
soci_wrappers.h
Go to the documentation of this file.
1
9#ifndef MANTISAPP_SOCI_WRAPPERS_H
10#define MANTISAPP_SOCI_WRAPPERS_H
11
12#include "utils.h"
13#include "../mantisbase.h"
15#include "soci/values.h"
16
17namespace mb {
18 inline soci::values json2SociValue(const json &entity, const json &fields) {
19 if (!fields.is_array()) throw std::invalid_argument("Fields must be an array");
20
21 soci::values vals;
22 // Bind parameters dynamically
23 for (const auto &field: fields) {
24 const auto field_name = field.at("name").get<std::string>();
25
26 if (field_name == "id" || field_name == "created" || field_name == "updated") {
27 continue;
28 }
29 // Skip fields that are not in the JSON object
30 if (!entity.contains(field_name)) continue;
31
32 // For password types, let's hash them before binding to DB
33 if (field_name == "password") {
34 // Extract password value and hash it
35 auto hashed_password = hashPassword(entity.at(field_name).get<std::string>());
36
37 // Add the hashed password to the soci::vals
38 vals.set(field_name, hashed_password);
39 continue;
40 }
41
42 // If the value is null, set i_null and continue
43 if (entity[field_name].is_null()) {
44 std::optional<int> val; // Set to optional, no value is set in db
45 vals.set(field_name, val, soci::i_null);
46 continue;
47 }
48
49 // For non-null values, set the value accordingly
50 const auto field_type = field.at("type").get<std::string>();
51 if (field_type == "xml" || field_type == "string" || field_type == "file") {
52 vals.set(field_name, entity.value(field_name, ""));
53 } else if (field_type == "double") {
54 vals.set(field_name, entity.value(field_name, 0.0));
55 } else if (field_type == "date") {
56 auto dt_str = entity.value(field_name, "");
57 if (dt_str.empty()) {
58 vals.set(field_name, 0, soci::i_null);
59 } else {
60 std::tm tm{};
61 std::istringstream ss{dt_str};
62 ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
63
64 vals.set(field_name, tm);
65 }
66 } else if (field_type == "int8") {
67 vals.set(field_name, static_cast<int8_t>(entity.value(field_name, 0)));
68 } else if (field_type == "uint8") {
69 vals.set(field_name, static_cast<uint8_t>(entity.value(field_name, 0)));
70 } else if (field_type == "int16") {
71 vals.set(field_name, static_cast<int16_t>(entity.value(field_name, 0)));
72 } else if (field_type == "uint16") {
73 vals.set(field_name, static_cast<uint16_t>(entity.value(field_name, 0)));
74 } else if (field_type == "int32") {
75 vals.set(field_name, static_cast<int32_t>(entity.value(field_name, 0)));
76 } else if (field_type == "uint32") {
77 vals.set(field_name, static_cast<uint32_t>(entity.value(field_name, 0)));
78 } else if (field_type == "int64") {
79 vals.set(field_name, static_cast<int64_t>(entity.value(field_name, 0)));
80 } else if (field_type == "uint64") {
81 vals.set(field_name, static_cast<uint64_t>(entity.value(field_name, 0)));
82 } else if (field_type == "blob") {
83 // TODO implement BLOB type
84 // vals.set(field_name, entity.value(field_name, sql->empty_blob()));
85 } else if (field_type == "json") {
86 vals.set(field_name, entity.value(field_name, json::object()));
87 } else if (field_type == "bool") {
88 vals.set(field_name, entity.value(field_name, false));
89 } else if (field_type == "files") {
90 vals.set(field_name, entity.value(field_name, json::array()));
91 }
92 }
93
94 return vals;
95 }
96
97 inline std::string getColumnType(const std::string &column_name, const std::vector<json> &fields) {
98 if (column_name.empty()) throw std::invalid_argument("Column name can't be empty!");
99
100 for (const auto &field: fields) {
101 if (field.value("name", "") == column_name)
102 return field.at("type").get<std::string>();
103 }
104
105 throw std::runtime_error("No field type found matching column `" + column_name + "'");
106 }
107
108 inline json sociRow2Json(const soci::row &row, const std::vector<json> &entity_fields) {
109 // Guard against empty reference schema fields
110 if (entity_fields.empty())
111 throw std::invalid_argument("Reference schema fields can't be empty!");
112
113 // Build response json object
114 json res_json;
115 for (size_t i = 0; i < row.size(); i++) {
116 const auto colName = row.get_properties(i).get_name();
117 const auto colType = getColumnType(colName, entity_fields);
118
119 // Check column type is valid type
120 if (colType.empty() || !EntitySchemaField::isValidFieldType(colType)) // Or not in expected types
121 {
122 // Throw an error for unknown types
123 throw std::runtime_error(std::format("Unknown column type `{}` for column `{}`", colType, colName));
124 }
125
126 // Handle null values immediately
127 if (row.get_indicator(i) == soci::i_null) {
128 // Handle null value in JSON
129 res_json[colName] = nullptr;
130 continue;
131 }
132
133 // Handle type conversions
134 if (colType == "xml" || colType == "string") {
135 res_json[colName] = row.get<std::string>(i, "");
136 } else if (colType == "double") {
137 res_json[colName] = row.get<double>(i);
138 } else if (colType == "date") {
139 res_json[colName] = mb::dbDateToString(row, i);
140 } else if (colType == "int8") {
141 res_json[colName] = row.get<int8_t>(i);
142 } else if (colType == "uint8") {
143 res_json[colName] = row.get<uint8_t>(i);
144 } else if (colType == "int16") {
145 res_json[colName] = row.get<int16_t>(i);
146 } else if (colType == "uint16") {
147 res_json[colName] = row.get<uint16_t>(i);
148 } else if (colType == "int32") {
149 res_json[colName] = row.get<int32_t>(i);
150 } else if (colType == "uint32") {
151 res_json[colName] = row.get<uint32_t>(i);
152 } else if (colType == "int64") {
153 res_json[colName] = row.get<int64_t>(i);
154 } else if (colType == "uint64") {
155 res_json[colName] = row.get<uint64_t>(i);
156 } else if (colType == "blob") {
157 // TODO ? How do we handle BLOB?
158 // j[colName] = row.get<std::string>(i);
159 } else if (colType == "json" || colType == "list") {
160 res_json[colName] = row.get<json>(i);
161 } else if (colType == "bool") {
162 res_json[colName] = row.get<bool>(i);
163 } else if (colType == "file") {
164 res_json[colName] = row.get<std::string>(i);
165 } else if (colType == "files") {
166 res_json[colName] = row.get<json>(i);
167 }
168 }
169
170 return res_json;
171 }
172}
173
174#endif //MANTISAPP_SOCI_WRAPPERS_H
static bool isValidFieldType(const std::string &type)
Check if field type is valid.
Definition entity_schema_field.cpp:368
Entity schema field definition and validation.
router.h
Definition auth.h:15
json sociRow2Json(const soci::row &row, const std::vector< json > &entity_fields)
Definition soci_wrappers.h:108
std::string getColumnType(const std::string &column_name, const std::vector< json > &fields)
Definition soci_wrappers.h:97
std::string dbDateToString(const soci::row &row, int index)
Convert database date value from SOCI row to string.
Definition date_utils.cpp:28
std::string hashPassword(const std::string &password)
Digests user password + a generated salt to yield a hashed password.
Definition auth_utils.cpp:8
soci::values json2SociValue(const json &entity, const json &fields)
Definition soci_wrappers.h:18
nlohmann::json json
Shorten JSON namespace.
Definition context_store.h:18
Collection of utility functions that are re-used across different files.