ORE Studio 0.0.4
Loading...
Searching...
No Matches
data_organization_handler.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2026 Marco Craveiro <marco.craveiro@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20#ifndef ORES_DQ_CORE_MESSAGING_DATA_ORGANIZATION_HANDLER_HPP
21#define ORES_DQ_CORE_MESSAGING_DATA_ORGANIZATION_HANDLER_HPP
22
23#include <optional>
24#include <stdexcept>
25#include <boost/uuid/string_generator.hpp>
26#include "ores.nats/domain/message.hpp"
27#include "ores.nats/service/client.hpp"
28#include "ores.database/domain/context.hpp"
29#include "ores.security/jwt/jwt_authenticator.hpp"
30#include "ores.service/messaging/handler_helpers.hpp"
31#include "ores.service/service/request_context.hpp"
32#include "ores.dq.api/messaging/data_organization_protocol.hpp"
33#include "ores.dq.core/service/data_organization_service.hpp"
34#include "ores.dq.core/service/dataset_service.hpp"
35#include "ores.logging/make_logger.hpp"
36
37namespace ores::dq::messaging {
38
39using ores::service::messaging::reply;
40using ores::service::messaging::decode;
41using ores::service::messaging::stamp;
42using ores::service::messaging::error_reply;
43using ores::service::messaging::has_permission;
44using namespace ores::logging;
45
46namespace {
47inline auto& data_organization_handler_lg() {
48 static auto instance = ores::logging::make_logger("ores.dq.messaging.data_organization_handler");
49 return instance;
50}
51} // namespace
52
53class data_organization_handler {
54public:
55 data_organization_handler(
58 std::optional<ores::security::jwt::jwt_authenticator> verifier)
59 : nats_(nats), ctx_(std::move(ctx)), verifier_(std::move(verifier)) {}
60
61 // =========================================================================
62 // Catalogs
63 // =========================================================================
64
65 void list_catalogs(ores::nats::message msg) {
66 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
67 auto req = decode<get_catalogs_request>(msg);
68 if (!req) {
69 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
70 return;
71 }
72 auto ctx_expected = ores::service::service::make_request_context(
73 ctx_, msg, verifier_);
74 if (!ctx_expected) {
75 error_reply(nats_, msg, ctx_expected.error());
76 return;
77 }
78 const auto& ctx = *ctx_expected;
79 service::data_organization_service svc(ctx);
80 try {
81 const auto items = svc.list_catalogs(
82 static_cast<std::uint32_t>(req->offset),
83 static_cast<std::uint32_t>(req->limit));
84 const auto count = svc.get_catalog_count();
85 get_catalogs_response resp;
86 resp.catalogs = items;
87 resp.total_available_count = static_cast<int>(count);
88 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
89 reply(nats_, msg, resp);
90 } catch (const std::exception& e) {
91 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
92 get_catalogs_response resp;
93 resp.total_available_count = 0;
94 reply(nats_, msg, resp);
95 }
96 }
97
98 void save_catalog(ores::nats::message msg) {
99 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
100 auto req = decode<save_catalog_request>(msg);
101 if (!req) {
102 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
103 return;
104 }
105 auto ctx_expected = ores::service::service::make_request_context(
106 ctx_, msg, verifier_);
107 if (!ctx_expected) {
108 error_reply(nats_, msg, ctx_expected.error());
109 return;
110 }
111 const auto& ctx = *ctx_expected;
112 if (!has_permission(ctx, "dq::catalogs:write")) {
113 error_reply(nats_, msg, ores::service::error_code::forbidden);
114 return;
115 }
116 service::data_organization_service svc(ctx);
117 try {
118 stamp(req->data, ctx);
119 svc.save_catalog(req->data);
120 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
121 reply(nats_, msg, save_catalog_response{true, {}});
122 } catch (const std::exception& e) {
123 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
124 reply(nats_, msg, save_catalog_response{false, e.what()});
125 }
126 }
127
128 void delete_catalogs(ores::nats::message msg) {
129 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
130 auto req = decode<delete_catalog_request>(msg);
131 if (!req) {
132 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
133 return;
134 }
135 auto ctx_expected = ores::service::service::make_request_context(
136 ctx_, msg, verifier_);
137 if (!ctx_expected) {
138 error_reply(nats_, msg, ctx_expected.error());
139 return;
140 }
141 const auto& ctx = *ctx_expected;
142 if (!has_permission(ctx, "dq::catalogs:delete")) {
143 error_reply(nats_, msg, ores::service::error_code::forbidden);
144 return;
145 }
146 service::data_organization_service svc(ctx);
147 try {
148 for (const auto& code : req->codes)
149 svc.remove_catalog(code);
150 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
151 reply(nats_, msg, delete_catalog_response{true, {}});
152 } catch (const std::exception& e) {
153 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
154 reply(nats_, msg, delete_catalog_response{false, e.what()});
155 }
156 }
157
158 void catalog_history(ores::nats::message msg) {
159 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
160 auto req = decode<get_catalog_history_request>(msg);
161 if (!req) {
162 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
163 return;
164 }
165 auto ctx_expected = ores::service::service::make_request_context(
166 ctx_, msg, verifier_);
167 if (!ctx_expected) {
168 error_reply(nats_, msg, ctx_expected.error());
169 return;
170 }
171 const auto& ctx = *ctx_expected;
172 service::data_organization_service svc(ctx);
173 try {
174 const auto history = svc.get_catalog_history(req->code);
175 get_catalog_history_response resp;
176 resp.success = true;
177 resp.history = history;
178 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
179 reply(nats_, msg, resp);
180 } catch (const std::exception& e) {
181 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
182 get_catalog_history_response resp;
183 resp.success = false;
184 resp.message = e.what();
185 reply(nats_, msg, resp);
186 }
187 }
188
189 // =========================================================================
190 // Data Domains
191 // =========================================================================
192
193 void list_data_domains(ores::nats::message msg) {
194 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
195 auto req = decode<get_data_domains_request>(msg);
196 if (!req) {
197 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
198 return;
199 }
200 auto ctx_expected = ores::service::service::make_request_context(
201 ctx_, msg, verifier_);
202 if (!ctx_expected) {
203 error_reply(nats_, msg, ctx_expected.error());
204 return;
205 }
206 const auto& ctx = *ctx_expected;
207 service::data_organization_service svc(ctx);
208 try {
209 const auto items = svc.list_data_domains();
210 get_data_domains_response resp;
211 resp.domains = items;
212 resp.total_available_count = static_cast<int>(items.size());
213 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
214 reply(nats_, msg, resp);
215 } catch (const std::exception& e) {
216 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
217 get_data_domains_response resp;
218 resp.total_available_count = 0;
219 reply(nats_, msg, resp);
220 }
221 }
222
223 void save_data_domain(ores::nats::message msg) {
224 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
225 auto req = decode<save_data_domain_request>(msg);
226 if (!req) {
227 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
228 return;
229 }
230 auto ctx_expected = ores::service::service::make_request_context(
231 ctx_, msg, verifier_);
232 if (!ctx_expected) {
233 error_reply(nats_, msg, ctx_expected.error());
234 return;
235 }
236 const auto& ctx = *ctx_expected;
237 if (!has_permission(ctx, "dq::data_domains:write")) {
238 error_reply(nats_, msg, ores::service::error_code::forbidden);
239 return;
240 }
241 service::data_organization_service svc(ctx);
242 try {
243 stamp(req->data, ctx);
244 svc.save_data_domain(req->data);
245 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
246 reply(nats_, msg, save_data_domain_response{true, {}});
247 } catch (const std::exception& e) {
248 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
249 reply(nats_, msg, save_data_domain_response{false, e.what()});
250 }
251 }
252
253 void delete_data_domains(ores::nats::message msg) {
254 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
255 auto req = decode<delete_data_domain_request>(msg);
256 if (!req) {
257 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
258 return;
259 }
260 auto ctx_expected = ores::service::service::make_request_context(
261 ctx_, msg, verifier_);
262 if (!ctx_expected) {
263 error_reply(nats_, msg, ctx_expected.error());
264 return;
265 }
266 const auto& ctx = *ctx_expected;
267 if (!has_permission(ctx, "dq::data_domains:delete")) {
268 error_reply(nats_, msg, ores::service::error_code::forbidden);
269 return;
270 }
271 service::data_organization_service svc(ctx);
272 try {
273 for (const auto& name : req->names)
274 svc.remove_data_domain(name);
275 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
276 reply(nats_, msg, delete_data_domain_response{true, {}});
277 } catch (const std::exception& e) {
278 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
279 reply(nats_, msg, delete_data_domain_response{false, e.what()});
280 }
281 }
282
283 void data_domain_history(ores::nats::message msg) {
284 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
285 auto req = decode<get_data_domain_history_request>(msg);
286 if (!req) {
287 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
288 return;
289 }
290 auto ctx_expected = ores::service::service::make_request_context(
291 ctx_, msg, verifier_);
292 if (!ctx_expected) {
293 error_reply(nats_, msg, ctx_expected.error());
294 return;
295 }
296 const auto& ctx = *ctx_expected;
297 service::data_organization_service svc(ctx);
298 try {
299 const auto history = svc.get_data_domain_history(req->name);
300 get_data_domain_history_response resp;
301 resp.success = true;
302 resp.history = history;
303 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
304 reply(nats_, msg, resp);
305 } catch (const std::exception& e) {
306 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
307 get_data_domain_history_response resp;
308 resp.success = false;
309 resp.message = e.what();
310 reply(nats_, msg, resp);
311 }
312 }
313
314 // =========================================================================
315 // Methodologies
316 // =========================================================================
317
318 void list_methodologies(ores::nats::message msg) {
319 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
320 auto req = decode<get_methodologies_request>(msg);
321 if (!req) {
322 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
323 return;
324 }
325 auto ctx_expected = ores::service::service::make_request_context(
326 ctx_, msg, verifier_);
327 if (!ctx_expected) {
328 error_reply(nats_, msg, ctx_expected.error());
329 return;
330 }
331 const auto& ctx = *ctx_expected;
332 service::dataset_service svc(ctx);
333 try {
334 const auto items = svc.list_methodologies(
335 static_cast<std::uint32_t>(req->offset),
336 static_cast<std::uint32_t>(req->limit));
337 const auto count = svc.get_methodology_count();
338 get_methodologies_response resp;
339 resp.methodologies = items;
340 resp.total_available_count = static_cast<int>(count);
341 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
342 reply(nats_, msg, resp);
343 } catch (const std::exception& e) {
344 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
345 get_methodologies_response resp;
346 resp.total_available_count = 0;
347 reply(nats_, msg, resp);
348 }
349 }
350
351 void save_methodology(ores::nats::message msg) {
352 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
353 auto req = decode<save_methodology_request>(msg);
354 if (!req) {
355 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
356 return;
357 }
358 auto ctx_expected = ores::service::service::make_request_context(
359 ctx_, msg, verifier_);
360 if (!ctx_expected) {
361 error_reply(nats_, msg, ctx_expected.error());
362 return;
363 }
364 const auto& ctx = *ctx_expected;
365 if (!has_permission(ctx, "dq::methodologies:write")) {
366 error_reply(nats_, msg, ores::service::error_code::forbidden);
367 return;
368 }
369 service::dataset_service svc(ctx);
370 try {
371 stamp(req->data, ctx);
372 svc.save_methodology(req->data);
373 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
374 reply(nats_, msg, save_methodology_response{true, {}});
375 } catch (const std::exception& e) {
376 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
377 reply(nats_, msg, save_methodology_response{false, e.what()});
378 }
379 }
380
381 void delete_methodologies(ores::nats::message msg) {
382 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
383 auto req = decode<delete_methodology_request>(msg);
384 if (!req) {
385 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
386 return;
387 }
388 auto ctx_expected = ores::service::service::make_request_context(
389 ctx_, msg, verifier_);
390 if (!ctx_expected) {
391 error_reply(nats_, msg, ctx_expected.error());
392 return;
393 }
394 const auto& ctx = *ctx_expected;
395 if (!has_permission(ctx, "dq::methodologies:delete")) {
396 error_reply(nats_, msg, ores::service::error_code::forbidden);
397 return;
398 }
399 service::dataset_service svc(ctx);
400 try {
401 for (const auto& code : req->codes)
402 svc.remove_methodology(boost::uuids::string_generator{}(code));
403 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
404 reply(nats_, msg, delete_methodology_response{true, {}});
405 } catch (const std::exception& e) {
406 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
407 reply(nats_, msg, delete_methodology_response{false, e.what()});
408 }
409 }
410
411 void methodology_history(ores::nats::message msg) {
412 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
413 auto req = decode<get_methodology_history_request>(msg);
414 if (!req) {
415 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
416 return;
417 }
418 auto ctx_expected = ores::service::service::make_request_context(
419 ctx_, msg, verifier_);
420 if (!ctx_expected) {
421 error_reply(nats_, msg, ctx_expected.error());
422 return;
423 }
424 const auto& ctx = *ctx_expected;
425 service::dataset_service svc(ctx);
426 try {
427 const auto history = svc.get_methodology_history(
428 boost::uuids::string_generator{}(req->code));
429 get_methodology_history_response resp;
430 resp.success = true;
431 resp.history = history;
432 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
433 reply(nats_, msg, resp);
434 } catch (const std::exception& e) {
435 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
436 get_methodology_history_response resp;
437 resp.success = false;
438 resp.message = e.what();
439 reply(nats_, msg, resp);
440 }
441 }
442
443 // =========================================================================
444 // Subject Areas
445 // =========================================================================
446
447 void list_subject_areas(ores::nats::message msg) {
448 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
449 auto req = decode<get_subject_areas_request>(msg);
450 if (!req) {
451 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
452 return;
453 }
454 auto ctx_expected = ores::service::service::make_request_context(
455 ctx_, msg, verifier_);
456 if (!ctx_expected) {
457 error_reply(nats_, msg, ctx_expected.error());
458 return;
459 }
460 const auto& ctx = *ctx_expected;
461 service::data_organization_service svc(ctx);
462 try {
463 const auto items = svc.list_subject_areas(
464 static_cast<std::uint32_t>(req->offset),
465 static_cast<std::uint32_t>(req->limit));
466 const auto count = svc.get_subject_area_count();
467 get_subject_areas_response resp;
468 resp.subject_areas = items;
469 resp.total_available_count = static_cast<int>(count);
470 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
471 reply(nats_, msg, resp);
472 } catch (const std::exception& e) {
473 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
474 get_subject_areas_response resp;
475 resp.total_available_count = 0;
476 reply(nats_, msg, resp);
477 }
478 }
479
480 void save_subject_area(ores::nats::message msg) {
481 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
482 auto req = decode<save_subject_area_request>(msg);
483 if (!req) {
484 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
485 return;
486 }
487 auto ctx_expected = ores::service::service::make_request_context(
488 ctx_, msg, verifier_);
489 if (!ctx_expected) {
490 error_reply(nats_, msg, ctx_expected.error());
491 return;
492 }
493 const auto& ctx = *ctx_expected;
494 if (!has_permission(ctx, "dq::subject_areas:write")) {
495 error_reply(nats_, msg, ores::service::error_code::forbidden);
496 return;
497 }
498 service::data_organization_service svc(ctx);
499 try {
500 stamp(req->data, ctx);
501 svc.save_subject_area(req->data);
502 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
503 reply(nats_, msg, save_subject_area_response{true, {}});
504 } catch (const std::exception& e) {
505 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
506 reply(nats_, msg, save_subject_area_response{false, e.what()});
507 }
508 }
509
510 void delete_subject_areas(ores::nats::message msg) {
511 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
512 auto req = decode<delete_subject_area_request>(msg);
513 if (!req) {
514 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
515 return;
516 }
517 auto ctx_expected = ores::service::service::make_request_context(
518 ctx_, msg, verifier_);
519 if (!ctx_expected) {
520 error_reply(nats_, msg, ctx_expected.error());
521 return;
522 }
523 const auto& ctx = *ctx_expected;
524 if (!has_permission(ctx, "dq::subject_areas:delete")) {
525 error_reply(nats_, msg, ores::service::error_code::forbidden);
526 return;
527 }
528 service::data_organization_service svc(ctx);
529 try {
530 for (const auto& key : req->keys)
531 svc.remove_subject_area(key.name, key.domain_name);
532 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
533 reply(nats_, msg, delete_subject_area_response{true, {}});
534 } catch (const std::exception& e) {
535 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
536 reply(nats_, msg, delete_subject_area_response{false, e.what()});
537 }
538 }
539
540 void subject_area_history(ores::nats::message msg) {
541 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Handling " << msg.subject;
542 auto req = decode<get_subject_area_history_request>(msg);
543 if (!req) {
544 BOOST_LOG_SEV(data_organization_handler_lg(), warn) << "Failed to decode: " << msg.subject;
545 return;
546 }
547 auto ctx_expected = ores::service::service::make_request_context(
548 ctx_, msg, verifier_);
549 if (!ctx_expected) {
550 error_reply(nats_, msg, ctx_expected.error());
551 return;
552 }
553 const auto& ctx = *ctx_expected;
554 service::data_organization_service svc(ctx);
555 try {
556 const auto history = svc.get_subject_area_history(
557 req->key.name, req->key.domain_name);
558 get_subject_area_history_response resp;
559 resp.success = true;
560 resp.history = history;
561 BOOST_LOG_SEV(data_organization_handler_lg(), debug) << "Completed " << msg.subject;
562 reply(nats_, msg, resp);
563 } catch (const std::exception& e) {
564 BOOST_LOG_SEV(data_organization_handler_lg(), error) << msg.subject << " failed: " << e.what();
565 get_subject_area_history_response resp;
566 resp.success = false;
567 resp.message = e.what();
568 reply(nats_, msg, resp);
569 }
570 }
571
572private:
573
576 std::optional<ores::security::jwt::jwt_authenticator> verifier_;
577};
578
579} // namespace ores::dq::messaging
580
581#endif
STL namespace.
Implements logging infrastructure for ORE Studio.
Definition boost_severity.hpp:28
@ forbidden
The caller is authenticated but lacks the required permission.
Context for the operations on a postgres database.
Definition context.hpp:47
A received NATS message.
Definition message.hpp:40
std::string subject
The subject the message was published to.
Definition message.hpp:44
NATS client: connection, pub/sub, request/reply, and JetStream.
Definition client.hpp:73