Full C++23 framework tree: runtime, observability, security, data, app, realtime, supervisor, clients, CLI, example plus enterprise tenancy, audit, operability, governance, supplychain. Postgres via docker compose for tests.
142 lines
5.5 KiB
C++
142 lines
5.5 KiB
C++
#include "App.hpp"
|
|
|
|
#include "../fw/Http.hpp"
|
|
#include "../fw/Router.hpp"
|
|
#include "../fw/Validate.hpp"
|
|
|
|
namespace molo::example::app {
|
|
|
|
namespace {
|
|
|
|
fw::OrmDb* pick_db(Services& services, const fw::Request& req) {
|
|
if (fw::parse_backend(req.query_or("backend", "sqlite")) ==
|
|
fw::OrmBackend::Postgres)
|
|
return services.postgres.get();
|
|
return services.sqlite.get();
|
|
}
|
|
|
|
std::string rows_json(const std::vector<fw::OrmRow>& rows) {
|
|
std::string out = "[";
|
|
bool first = true;
|
|
for (const auto& r : rows) {
|
|
if (!first) out += ",";
|
|
first = false;
|
|
out += "{";
|
|
bool fc = true;
|
|
for (const auto& [k, v] : r.columns) {
|
|
if (!fc) out += ",";
|
|
fc = false;
|
|
out += "\"" + fw::json_escape(k) + "\":\"" + fw::json_escape(v) + "\"";
|
|
}
|
|
out += "}";
|
|
}
|
|
return out + "]";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void register_data_demo(fw::Router& router, Services& services) {
|
|
fw::OrmDb* sqlite = services.sqlite.get();
|
|
fw::OrmDb* postgres = services.postgres.get();
|
|
fw::KvStore* kv = services.kv.get();
|
|
fw::Cache* cache = services.cache.get();
|
|
fw::Pubsub* pubsub = services.pubsub.get();
|
|
|
|
router.get("/orm/users", "orm-users", [sqlite, postgres](fw::Request req) {
|
|
fw::OrmDb* db = req.query_or("backend", "sqlite") == "postgres" ? postgres
|
|
: sqlite;
|
|
fw::OrmResult res = db->select_all("users");
|
|
std::string body = "{\"backend\":\"" + fw::backend_name(db->backend()) +
|
|
"\",\"healed\":" + (res.healed ? "true" : "false") +
|
|
",\"note\":\"" + fw::json_escape(res.healed_note) +
|
|
"\",\"rows\":" + rows_json(res.rows) + "}";
|
|
return fw::JsonResponse(body, 200);
|
|
});
|
|
router.post("/orm/users", "orm-users-post",
|
|
[sqlite, postgres](fw::Request req) {
|
|
fw::OrmDb* db = req.query_or("backend", "sqlite") == "postgres"
|
|
? postgres
|
|
: sqlite;
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
fw::OrmResult res = db->insert("users", input);
|
|
if (!res.ok)
|
|
return fw::JsonResponse("{\"error\":\"store full\"}", 500);
|
|
std::string body =
|
|
"{\"backend\":\"" + fw::backend_name(db->backend()) +
|
|
"\",\"healed\":" + (res.healed ? "true" : "false") +
|
|
",\"note\":\"" + fw::json_escape(res.healed_note) +
|
|
"\",\"rows\":" + rows_json(res.rows) + "}";
|
|
return fw::JsonResponse(body, 201);
|
|
});
|
|
|
|
router.get("/kv/items", "kv-get", [kv](fw::Request req) {
|
|
std::string key = req.query_or("key", "");
|
|
if (key.empty()) return fw::JsonResponse("{\"error\":\"key required\"}", 400);
|
|
std::string value;
|
|
if (!kv->get(key, value))
|
|
return fw::JsonResponse("{\"error\":\"not found\"}", 404);
|
|
return fw::JsonResponse("{\"key\":\"" + fw::json_escape(key) +
|
|
"\",\"value\":\"" + fw::json_escape(value) +
|
|
"\"}",
|
|
200);
|
|
});
|
|
router.post("/kv/items", "kv-set", [kv](fw::Request req) {
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
if (!input.count("key") || !input.count("value"))
|
|
return fw::JsonResponse("{\"error\":\"key and value required\"}", 400);
|
|
if (!kv->set(input["key"], input["value"]))
|
|
return fw::JsonResponse("{\"error\":\"store full\"}", 500);
|
|
return fw::JsonResponse("{\"stored\":\"" + fw::json_escape(input["key"]) +
|
|
"\"}",
|
|
201);
|
|
});
|
|
|
|
router.get("/cache/lookup", "cache", [cache](fw::Request req) {
|
|
std::string key = req.query_or("key", "demo");
|
|
std::string value;
|
|
bool hit = cache->lookup(key, value, 1000);
|
|
if (!hit) {
|
|
value = "computed-for-" + key;
|
|
cache->put(key, value, 60, 1000);
|
|
return fw::JsonResponse(
|
|
"{\"key\":\"" + fw::json_escape(key) + "\",\"hit\":false,\"value\":\"" +
|
|
fw::json_escape(value) + "\"}",
|
|
200);
|
|
}
|
|
return fw::JsonResponse(
|
|
"{\"key\":\"" + fw::json_escape(key) + "\",\"hit\":true,\"value\":\"" +
|
|
fw::json_escape(value) + "\"}",
|
|
200);
|
|
});
|
|
|
|
router.post("/pubsub/publish", "pubsub", [pubsub](fw::Request req) {
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
std::string topic = input.count("topic") ? input["topic"] : "news";
|
|
std::string message = input.count("message") ? input["message"] : "hello";
|
|
std::size_t delivered = pubsub->publish(topic, message);
|
|
return fw::JsonResponse("{\"topic\":\"" + fw::json_escape(topic) +
|
|
"\",\"delivered\":" +
|
|
std::to_string(delivered) + "}",
|
|
200);
|
|
});
|
|
router.get("/pubsub/publish", "pubsub-history", [pubsub](fw::Request req) {
|
|
std::string topic = req.query_or("topic", "news");
|
|
auto hist = pubsub->history(topic);
|
|
std::string body = "{\"topic\":\"" + fw::json_escape(topic) +
|
|
"\",\"history\":[";
|
|
bool first = true;
|
|
for (const auto& m : hist) {
|
|
if (!first) body += ",";
|
|
first = false;
|
|
body += "\"" + fw::json_escape(m) + "\"";
|
|
}
|
|
return fw::JsonResponse(body + "]}", 200);
|
|
});
|
|
(void)pick_db;
|
|
}
|
|
|
|
} // namespace molo::example::app
|