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 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 102 of file JsonMappingReader.cpp.

102 {
103 std::string draftPath = getDraftPath(mapFilePath);
104 if (!fs::exists(draftPath))
105 return nlohmann::json();
106
107 nlohmann::json j;
108
109 // 1. Inject creation timestamp into draft
110 try {
111 std::ifstream f(draftPath);
112 f >> j;
113 f.close();
114
115 auto now = std::chrono::system_clock::now();
116 std::time_t now_c = std::chrono::system_clock::to_time_t(now);
117 std::stringstream ss;
118 ss << std::put_time(std::gmtime(&now_c), "%Y-%m-%dT%H:%M:%SZ");
119
120 if (!j.contains("meta"))
121 j["meta"] = nlohmann::json::object();
122 j["meta"]["created"] = ss.str();
123 j["meta"]["version"] = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
124
125 std::ofstream out(draftPath, std::ios::trunc);
126 out << j.dump(2);
127 out.close();
128 } catch (const std::exception& e) {
129 VLOG(1) << "Failed to inject metadata into draft: " << e.what();
130 }
131
132 // 2. Backup current active file
133 if (fs::exists(mapFilePath)) {
134 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
135 if (!fs::exists(versionDir)) {
136 fs::create_directories(versionDir);
137 }
138
139 auto now = std::chrono::system_clock::now();
140 auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
141 std::string filename = fs::path(mapFilePath).filename().string();
142 std::string backupPath = versionDir / (filename + "." + std::to_string(timestamp));
143
144 fs::copy_file(mapFilePath, backupPath, fs::copy_options::overwrite_existing);
145
146 // 3. Prune old versions (Keep last 50)
147 try {
148 std::vector<fs::path> versions;
149 for (const auto& entry : fs::directory_iterator(versionDir)) {
150 if (entry.path().filename().string().starts_with(filename + ".")) {
151 versions.push_back(entry.path());
152 }
153 }
154 if (versions.size() > 50) {
155 std::sort(versions.begin(), versions.end(), [](const fs::path& a, const fs::path& b) {
156 return fs::last_write_time(a) < fs::last_write_time(b); // Oldest first
157 });
158 for (size_t i = 0; i < versions.size() - 50; ++i) {
159 fs::remove(versions[i]);
160 }
161 }
162 } catch (...) {
163 }
164 }
165
166 // 4. Erase draft file
167 fs::remove(draftPath);
168
169 return j;
170 }
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 172 of file JsonMappingReader.cpp.

172 {
173 std::string draftPath = getDraftPath(mapFilePath);
174 if (fs::exists(draftPath)) {
175 fs::remove(draftPath);
176 }
177 }

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 71 of file JsonMappingReader.cpp.

71 {
72 return mapFilePath + ".draft";
73 }

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 179 of file JsonMappingReader.cpp.

179 {
180 std::vector<VersionEntry> history;
181 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
182 std::string baseName = fs::path(mapFilePath).filename().string();
183
184 if (!fs::exists(versionDir))
185 return history;
186
187 for (const auto& entry : fs::directory_iterator(versionDir)) {
188 if (entry.path().filename().string().starts_with(baseName + ".")) {
189 VersionEntry v;
190 v.filename = entry.path().string();
191 // Extract ID (timestamp) from filename extension
192 v.id = entry.path().extension().string().substr(1);
193
194 // Peek inside JSON to get the comment
195 try {
196 std::ifstream f(v.filename);
197 nlohmann::json j;
198 f >> j;
199 if (j.contains("meta")) {
200 if (j["meta"].contains("comment"))
201 v.comment = j["meta"]["comment"];
202 if (j["meta"].contains("created"))
203 v.date = j["meta"]["created"];
204 }
205 } catch (...) {
206 }
207
208 // Fallback date if not in meta
209 if (v.date.empty()) {
210 try {
211 long long ts = std::stoll(v.id);
212 std::time_t t = static_cast<std::time_t>(ts);
213 std::stringstream ss;
214 ss << std::put_time(std::gmtime(&t), "%Y-%m-%dT%H:%M:%SZ");
215 v.date = ss.str();
216 } catch (...) {
217 v.date = "Unknown";
218 }
219 }
220
221 history.push_back(v);
222 }
223 }
224 // Sort by ID (descending)
225 std::sort(history.begin(), history.end(), [](const VersionEntry& a, const VersionEntry& b) {
226 // String comparison of timestamps works if they are same length, but better to be safe
227 try {
228 return std::stoll(a.id) > std::stoll(b.id);
229 } catch (...) {
230 return a.id > b.id;
231 }
232 });
233 return history;
234 }

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 83 of file JsonMappingReader.cpp.

83 {
84 std::string draftPath = getDraftPath(mapFilePath);
85 if (fs::exists(draftPath)) {
86 std::ifstream f(draftPath);
87 if (f) {
88 nlohmann::json j;
89 f >> j;
90 return j;
91 }
92 }
93 // Fallback to active file
94 std::ifstream f(mapFilePath);
95 if (!f)
96 throw std::runtime_error("Cannot open mapping file: " + mapFilePath);
97 nlohmann::json j;
98 f >> j;
99 return j;
100 }

References getDraftPath().

Here is the call graph for this function:

◆ rollbackTo()

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

Definition at line 236 of file JsonMappingReader.cpp.

236 {
237 nlohmann::json j;
238
239 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
240 std::string baseName = fs::path(mapFilePath).filename().string();
241 fs::path backupPath = versionDir / (baseName + "." + versionId);
242
243 if (!fs::exists(backupPath)) {
244 throw std::runtime_error("Version not found: " + versionId);
245 }
246
247 // Validate before rollback
248 try {
249 std::ifstream f(backupPath);
250 f >> j;
252 } catch (const std::exception& e) {
253 throw std::runtime_error(std::string("Cannot rollback: Version is invalid against current schema: ") + e.what());
254 }
255
256 // Overwrite active file
257 fs::copy_file(backupPath, mapFilePath, fs::copy_options::overwrite_existing);
258
259 // Delete any existing draft to avoid confusion
260 discardDraft(mapFilePath);
261
262 return j;
263 }
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 75 of file JsonMappingReader.cpp.

75 {
76 std::ofstream out(getDraftPath(mapFilePath), std::ios::trunc);
77 if (!out) {
78 throw std::runtime_error("Cannot open draft file for writing: " + getDraftPath(mapFilePath));
79 }
80 out << content.dump(2) << std::endl;
81 }
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: