168 lines
7.6 KiB
C++
168 lines
7.6 KiB
C++
#include "adminive/adapters/nlohmann_json.hpp"
|
|
#include "adminive/adapters/httplib.hpp"
|
|
#include "adminive_test.hpp"
|
|
#include <atomic>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
namespace httplib_adapter_test {
|
|
using Json = nlohmann::json;
|
|
struct Config {
|
|
std::string name{"initial"};
|
|
int count{1};
|
|
};
|
|
struct Item_Status {
|
|
std::string state{"ready"};
|
|
};
|
|
struct Overview_Status {
|
|
std::string user;
|
|
};
|
|
struct Row {
|
|
std::string name;
|
|
bool enabled{true};
|
|
int score{};
|
|
Item_Status status() {
|
|
return {};
|
|
}
|
|
};
|
|
}
|
|
namespace adminive {
|
|
template <>
|
|
struct Type_Descriptor<httplib_adapter_test::Config> {
|
|
static auto get() {
|
|
using T = httplib_adapter_test::Config;
|
|
return object<T>("httplib_config", ADMINIVE_FIELD(T, name).editable(), ADMINIVE_FIELD(T, count).editable());
|
|
}
|
|
};
|
|
template <>
|
|
struct Type_Descriptor<httplib_adapter_test::Item_Status> {
|
|
static auto get() {
|
|
using T = httplib_adapter_test::Item_Status;
|
|
return object<T>("httplib_item_status", ADMINIVE_FIELD(T, state));
|
|
}
|
|
};
|
|
template <>
|
|
struct Type_Descriptor<httplib_adapter_test::Overview_Status> {
|
|
static auto get() {
|
|
using T = httplib_adapter_test::Overview_Status;
|
|
return object<T>("httplib_overview_status", ADMINIVE_FIELD(T, user));
|
|
}
|
|
};
|
|
template <>
|
|
struct Type_Descriptor<httplib_adapter_test::Row> {
|
|
static auto get() {
|
|
using T = httplib_adapter_test::Row;
|
|
return object<T>("httplib_row", ADMINIVE_FIELD(T, name).creatable().editable(), ADMINIVE_FIELD(T, enabled).creatable().editable(), ADMINIVE_FIELD(T, score).creatable().editable());
|
|
}
|
|
};
|
|
template <>
|
|
struct Type_View_Descriptor<httplib_adapter_test::Row> {
|
|
static Table_View table() {
|
|
using T = httplib_adapter_test::Row;
|
|
return table_view<T>(column<&T::name>("Name").sort().search(), column<&T::enabled>("Enabled").filter(), column<&T::score>("Score").sort()).reorderable();
|
|
}
|
|
};
|
|
}
|
|
namespace {
|
|
using httplib_adapter_test::Json;
|
|
Json body_json(const httplib::Result& result) {
|
|
ADMINIVE_CHECK(result);
|
|
return Json::parse(result->body);
|
|
}
|
|
void check_status(const httplib::Result& result, int status) {
|
|
ADMINIVE_CHECK(result);
|
|
ADMINIVE_CHECK(result->status == status);
|
|
}
|
|
}
|
|
int main() {
|
|
using namespace httplib_adapter_test;
|
|
Config config;
|
|
std::string committed_user;
|
|
std::string committed_request_id;
|
|
std::string committed_remote;
|
|
adminive::Resource_Transaction<Config> transaction;
|
|
transaction.commit = [&](const Config&, const adminive::Request_Context& context) {
|
|
committed_user = context.user;
|
|
committed_request_id = context.request_id;
|
|
committed_remote = context.remote_address;
|
|
};
|
|
adminive::Http_Resource<Config, Json> resource(config, "/config", transaction);
|
|
adminive::Http_Status_Resource<Overview_Status, Json> status("/status", [](const adminive::Request_Context& context) {
|
|
if(context.user == "status-error") {
|
|
throw std::runtime_error("status failed");
|
|
}
|
|
return Overview_Status{context.user};
|
|
}, 25);
|
|
adminive::Http_Collection_Resource<Row, Json> collection("/rows", {Row{"alpha", true, 70}, Row{"beta", false, 90}, Row{"gamma", true, 50}});
|
|
collection.register_status<&Row::status>(25);
|
|
httplib::Server server;
|
|
adminive::Httplib_Bind_Options options;
|
|
options.context_factory = [](const httplib::Request& request) {
|
|
const std::string user = request.get_header_value("X-User");
|
|
if(user == "context-error") {
|
|
throw std::runtime_error("context failed");
|
|
}
|
|
adminive::Request_Context context;
|
|
context.user = user;
|
|
context.request_id = request.get_header_value("X-Request-Id");
|
|
context.remote_address = request.remote_addr;
|
|
context.attributes.emplace("transport", "httplib");
|
|
return context;
|
|
};
|
|
resource.bind(server, options);
|
|
status.bind(server, options);
|
|
collection.bind(server);
|
|
const int port = server.bind_to_any_port("127.0.0.1");
|
|
ADMINIVE_CHECK(port > 0);
|
|
std::thread thread([&] {
|
|
ADMINIVE_CHECK(server.listen_after_bind());
|
|
});
|
|
httplib::Client client("127.0.0.1", port);
|
|
check_status(client.Get("/config/descriptor"), 200);
|
|
ADMINIVE_CHECK(body_json(client.Get("/config/view")).at("data").at("kind") == "form");
|
|
ADMINIVE_CHECK(body_json(client.Get("/config/data")).at("data").at("name") == "initial");
|
|
check_status(client.Get("/config/amis"), 200);
|
|
httplib::Headers headers{{"X-User", "alice"}, {"X-Request-Id", "request-42"}};
|
|
const auto update = client.Post("/config/data", headers, R"({"name":"updated","count":9})", "application/json");
|
|
check_status(update, 200);
|
|
ADMINIVE_CHECK(config.name == "updated");
|
|
ADMINIVE_CHECK(config.count == 9);
|
|
ADMINIVE_CHECK(committed_user == "alice");
|
|
ADMINIVE_CHECK(committed_request_id == "request-42");
|
|
ADMINIVE_CHECK(committed_remote == "127.0.0.1");
|
|
httplib::Headers context_error_headers{{"X-User", "context-error"}};
|
|
check_status(client.Post("/config/data", context_error_headers, R"({"name":"not-applied"})", "application/json"), 500);
|
|
ADMINIVE_CHECK(config.name == "updated");
|
|
check_status(client.Get("/status/descriptor"), 200);
|
|
check_status(client.Get("/status/amis"), 200);
|
|
ADMINIVE_CHECK(body_json(client.Get("/status/data", headers)).at("data").at("user").at("value") == "alice");
|
|
httplib::Headers status_error_headers{{"X-User", "status-error"}};
|
|
check_status(client.Get("/status/data", status_error_headers), 500);
|
|
check_status(client.Get("/status/data", context_error_headers), 500);
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows/descriptor")).at("data").at("name") == "httplib_row");
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows/view")).at("data").at("kind") == "table");
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows?perPage=2&page=1&orderBy=score&orderDir=desc")).at("data").at("items").at(0).at("name") == "beta");
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows?name=alp")).at("data").at("total") == 1);
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows?enabled=true")).at("data").at("total") == 2);
|
|
check_status(client.Get("/rows?score=90"), 400);
|
|
check_status(client.Get("/rows?unknown=value"), 400);
|
|
check_status(client.Get("/rows?orderBy=enabled&orderDir=asc"), 400);
|
|
check_status(client.Get("/rows?orderBy=score&orderDir=sideways"), 400);
|
|
const auto created = body_json(client.Post("/rows", R"({"name":"delta","enabled":true,"score":80})", "application/json"));
|
|
const auto created_id = created.at("data").at("id").get<std::uint64_t>();
|
|
ADMINIVE_CHECK(created.at("data").at("name") == "delta");
|
|
ADMINIVE_CHECK(body_json(client.Get(("/rows/" + std::to_string(created_id)).c_str())).at("data").at("score") == 80);
|
|
check_status(client.Put(("/rows/" + std::to_string(created_id)).c_str(), R"({"score":81})", "application/json"), 200);
|
|
ADMINIVE_CHECK(body_json(client.Get(("/rows/" + std::to_string(created_id)).c_str())).at("data").at("score") == 81);
|
|
check_status(client.Patch(("/rows/" + std::to_string(created_id)).c_str(), R"({"name":"delta-patched"})", "application/json"), 200);
|
|
ADMINIVE_CHECK(body_json(client.Get(("/rows/" + std::to_string(created_id) + "/status").c_str())).at("data").at("state").at("value") == "ready");
|
|
check_status(client.Post("/rows/order", R"({"ids":[3,2,1]})", "application/json"), 200);
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows?orderBy=id&orderDir=asc")).at("data").at("total") == 4);
|
|
check_status(client.Delete(("/rows/" + std::to_string(created_id)).c_str()), 200);
|
|
ADMINIVE_CHECK(body_json(client.Get("/rows")).at("data").at("total") == 3);
|
|
server.stop();
|
|
thread.join();
|
|
return 0;
|
|
}
|