MQTTSuite
Loading...
Searching...
No Matches
mqtt::lib::JsonMappingReader Class Reference

#include <JsonMappingReader.h>

Collaboration diagram for mqtt::lib::JsonMappingReader:

Classes

struct  VersionEntry

Public Member Functions

 JsonMappingReader ()=delete

Static Public Member Functions

static nlohmann::json readMappingFromFile (const std::string &mapFilePath)
static void saveDraft (const std::string &mapFilePath, const nlohmann::json &content)
static nlohmann::json readDraftOrActive (const std::string &mapFilePath)
static nlohmann::json deployDraft (const std::string &mapFilePath)
static void discardDraft (const std::string &mapFilePath)
static std::string getDraftPath (const std::string &mapFilePath)
static std::vector< VersionEntrygetHistory (const std::string &mapFilePath)
static nlohmann::json rollbackTo (const std::string &mapFilePath, const std::string &versionId)

Static Private Member Functions

static nlohmann::json getDefaultPatch (const nlohmann::json &inputJson)

Detailed Description

Definition at line 57 of file JsonMappingReader.h.

Constructor & Destructor Documentation

◆ JsonMappingReader()

mqtt::lib::JsonMappingReader::JsonMappingReader ( )
delete

Member Function Documentation

◆ deployDraft()

nlohmann::json mqtt::lib::JsonMappingReader::deployDraft ( const std::string & mapFilePath)
static

Definition at line 129 of file JsonMappingReader.cpp.

129 {
130 std::string draftPath = getDraftPath(mapFilePath);
131 if (!fs::exists(draftPath))
132 return nlohmann::json();
133
134 nlohmann::json j;
135
136 // 1. Inject creation timestamp into draft
137 try {
138 std::ifstream f(draftPath);
139 f >> j;
140 f.close();
141
142 auto now = std::chrono::system_clock::now();
143 std::time_t now_c = std::chrono::system_clock::to_time_t(now);
144 std::stringstream ss;
145 ss << std::put_time(std::gmtime(&now_c), "%Y-%m-%dT%H:%M:%SZ");
146
147 if (!j.contains("meta"))
148 j["meta"] = nlohmann::json::object();
149 j["meta"]["created"] = ss.str();
150 j["meta"]["version"] = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
151
152 std::ofstream out(draftPath, std::ios::trunc);
153 out << j.dump(2);
154 out.close();
155 } catch (const std::exception& e) {
156 VLOG(1) << "Failed to inject metadata into draft: " << e.what();
157 }
158
159 // 2. Backup current active file
160 if (fs::exists(mapFilePath)) {
161 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
162 if (!fs::exists(versionDir)) {
163 fs::create_directories(versionDir);
164 }
165
166 auto now = std::chrono::system_clock::now();
167 auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
168 std::string filename = fs::path(mapFilePath).filename().string();
169 std::string backupPath = versionDir / (filename + "." + std::to_string(timestamp));
170
171 fs::copy_file(mapFilePath, backupPath, fs::copy_options::overwrite_existing);
172
173 // 3. Prune old versions (Keep last 50)
174 try {
175 std::vector<fs::path> versions;
176 for (const auto& entry : fs::directory_iterator(versionDir)) {
177 if (entry.path().filename().string().starts_with(filename + ".")) {
178 versions.push_back(entry.path());
179 }
180 }
181 if (versions.size() > 50) {
182 std::sort(versions.begin(), versions.end(), [](const fs::path& a, const fs::path& b) {
183 return fs::last_write_time(a) < fs::last_write_time(b); // Oldest first
184 });
185 for (size_t i = 0; i < versions.size() - 50; ++i) {
186 fs::remove(versions[i]);
187 }
188 }
189 } catch (...) {
190 }
191
192 // 4. Promote draft to active
193 fs::rename(draftPath, mapFilePath);
194 } else {
195 fs::remove(draftPath);
196 }
197
198 return j;
199 }
static std::string getDraftPath(const std::string &mapFilePath)

References getDraftPath().

Referenced by mqtt::lib::admin::makeMappingAdminRouter().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ discardDraft()

void mqtt::lib::JsonMappingReader::discardDraft ( const std::string & mapFilePath)
static

Definition at line 201 of file JsonMappingReader.cpp.

201 {
202 std::string draftPath = getDraftPath(mapFilePath);
203 if (fs::exists(draftPath)) {
204 fs::remove(draftPath);
205 }
206 }

References getDraftPath().

Referenced by rollbackTo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getDefaultPatch()

nlohmann::json mqtt::lib::JsonMappingReader::getDefaultPatch ( const nlohmann::json & inputJson)
staticprivate

◆ getDraftPath()

std::string mqtt::lib::JsonMappingReader::getDraftPath ( const std::string & mapFilePath)
static

Definition at line 98 of file JsonMappingReader.cpp.

98 {
99 return mapFilePath + ".draft";
100 }

Referenced by deployDraft(), discardDraft(), mqtt::lib::admin::makeMappingAdminRouter(), readDraftOrActive(), and saveDraft().

Here is the caller graph for this function:

◆ getHistory()

std::vector< JsonMappingReader::VersionEntry > mqtt::lib::JsonMappingReader::getHistory ( const std::string & mapFilePath)
static

Definition at line 208 of file JsonMappingReader.cpp.

208 {
209 std::vector<VersionEntry> history;
210 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
211 std::string baseName = fs::path(mapFilePath).filename().string();
212
213 if (!fs::exists(versionDir))
214 return history;
215
216 for (const auto& entry : fs::directory_iterator(versionDir)) {
217 if (entry.path().filename().string().starts_with(baseName + ".")) {
218 VersionEntry v;
219 v.filename = entry.path().string();
220 // Extract ID (timestamp) from filename extension
221 v.id = entry.path().extension().string().substr(1);
222
223 // Peek inside JSON to get the comment
224 try {
225 std::ifstream f(v.filename);
226 nlohmann::json j;
227 f >> j;
228 if (j.contains("meta")) {
229 if (j["meta"].contains("comment"))
230 v.comment = j["meta"]["comment"];
231 if (j["meta"].contains("created"))
232 v.date = j["meta"]["created"];
233 }
234 } catch (...) {
235 }
236
237 // Fallback date if not in meta
238 if (v.date.empty()) {
239 try {
240 long long ts = std::stoll(v.id);
241 std::time_t t = static_cast<std::time_t>(ts);
242 std::stringstream ss;
243 ss << std::put_time(std::gmtime(&t), "%Y-%m-%dT%H:%M:%SZ");
244 v.date = ss.str();
245 } catch (...) {
246 v.date = "Unknown";
247 }
248 }
249
250 history.push_back(v);
251 }
252 }
253 // Sort by ID (descending)
254 std::sort(history.begin(), history.end(), [](const VersionEntry& a, const VersionEntry& b) {
255 // String comparison of timestamps works if they are same length, but better to be safe
256 try {
257 return std::stoll(a.id) > std::stoll(b.id);
258 } catch (...) {
259 return a.id > b.id;
260 }
261 });
262 return history;
263 }

References mqtt::lib::JsonMappingReader::VersionEntry::comment, mqtt::lib::JsonMappingReader::VersionEntry::date, mqtt::lib::JsonMappingReader::VersionEntry::filename, and mqtt::lib::JsonMappingReader::VersionEntry::id.

Referenced by mqtt::lib::admin::makeMappingAdminRouter().

Here is the caller graph for this function:

◆ readDraftOrActive()

nlohmann::json mqtt::lib::JsonMappingReader::readDraftOrActive ( const std::string & mapFilePath)
static

Definition at line 110 of file JsonMappingReader.cpp.

110 {
111 std::string draftPath = getDraftPath(mapFilePath);
112 if (fs::exists(draftPath)) {
113 std::ifstream f(draftPath);
114 if (f) {
115 nlohmann::json j;
116 f >> j;
117 return j;
118 }
119 }
120 // Fallback to active file
121 std::ifstream f(mapFilePath);
122 if (!f)
123 throw std::runtime_error("Cannot open mapping file: " + mapFilePath);
124 nlohmann::json j;
125 f >> j;
126 return j;
127 }

References getDraftPath().

Here is the call graph for this function:

◆ readMappingFromFile()

nlohmann::json mqtt::lib::JsonMappingReader::readMappingFromFile ( const std::string & mapFilePath)
static

Definition at line 71 of file JsonMappingReader.cpp.

71 {
72 nlohmann::json mapFileJson;
73
74 if (!mapFilePath.empty()) {
75 std::ifstream mapFile(mapFilePath);
76
77 if (mapFile.is_open()) {
78 VLOG(1) << "MappingFilePath: " << mapFilePath;
79
80 try {
81 mapFile >> mapFileJson;
82 } catch (const std::exception& e) {
83 mapFile.close();
84
85 VLOG(1) << "JSON map file parsing failed: " << e.what() << " at " << mapFile.tellg();
86 throw std::runtime_error("JSON map file parsing faile at: " + std::to_string(mapFile.tellg()) + "\nWhat: " + e.what());
87 }
88
89 mapFile.close();
90 } else {
91 VLOG(1) << "MappingFile: " << mapFilePath << " not found";
92 }
93 }
94
95 return mapFileJson;
96 }

Referenced by mqtt::lib::ConfigApplication::ConfigApplication(), and mqtt::lib::ConfigApplication::setMappingFile().

Here is the caller graph for this function:

◆ rollbackTo()

nlohmann::json mqtt::lib::JsonMappingReader::rollbackTo ( const std::string & mapFilePath,
const std::string & versionId )
static

Definition at line 265 of file JsonMappingReader.cpp.

265 {
266 nlohmann::json j;
267
268 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
269 std::string baseName = fs::path(mapFilePath).filename().string();
270 fs::path backupPath = versionDir / (baseName + "." + versionId);
271
272 if (!fs::exists(backupPath)) {
273 throw std::runtime_error("Version not found: " + versionId);
274 }
275
276 // Validate before rollback
277 try {
278 std::ifstream f(backupPath);
279 f >> j;
281 } catch (const std::exception& e) {
282 throw std::runtime_error(std::string("Cannot rollback: Version is invalid against current schema: ") + e.what());
283 }
284
285 // Overwrite active file
286 fs::copy_file(backupPath, mapFilePath, fs::copy_options::overwrite_existing);
287
288 // Delete any existing draft to avoid confusion
289 discardDraft(mapFilePath);
290
291 return j;
292 }
static void discardDraft(const std::string &mapFilePath)
static const nlohmann::json validate(const nlohmann::json &json)

References discardDraft(), and mqtt::lib::MqttMapper::validate().

Referenced by mqtt::lib::admin::makeMappingAdminRouter().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ saveDraft()

void mqtt::lib::JsonMappingReader::saveDraft ( const std::string & mapFilePath,
const nlohmann::json & content )
static

Definition at line 102 of file JsonMappingReader.cpp.

102 {
103 std::ofstream out(getDraftPath(mapFilePath), std::ios::trunc);
104 if (!out) {
105 throw std::runtime_error("Cannot open draft file for writing: " + getDraftPath(mapFilePath));
106 }
107 out << content.dump(2) << std::endl;
108 }
static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance)

References getDraftPath().

Referenced by mqtt::lib::admin::makeMappingAdminRouter().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following files: