#include "adminive/adapters/boost_pfr.hpp" #include "adminive/adapters/nlohmann_json.hpp" #include "adminive/http.hpp" #include "adminive_test.hpp" #include #include #include namespace advanced_test { using Json = nlohmann::json; struct Nested_Config { int count{1}; }; struct Runtime_Model { Nested_Config nested; std::string name{"default"}; }; class Runtime_Config { public: Runtime_Config() = default; Runtime_Config(const Runtime_Config&) = delete; Runtime_Config& operator=(const Runtime_Config&) = delete; int count() const noexcept { return count_; } const std::string& name() const noexcept { return name_; } void apply(const Runtime_Model& value) { count_ = value.nested.count; name_ = value.name; ++apply_count_; } int apply_count() const noexcept { return apply_count_; } private: int count_{1}; int apply_count_{}; std::string name_{"default"}; }; struct Base_A { int alpha{3}; }; struct Base_B { std::string beta{"base"}; }; struct Inherited_Config : Base_A, Base_B {}; struct Editable_Reflected_Fields { template auto operator()(Field value) const { return value.editable(); } }; struct Color_Code { std::string value{"#112233"}; }; struct Control_Config { Color_Code color; }; struct Serial_Device { std::string port{"COM1"}; int baud_rate{115200}; Color_Code color; }; struct Network_Device { std::string host{"127.0.0.1"}; int port{9000}; }; using Device = std::variant; struct Device_Config { Device device{Serial_Device{}}; }; struct Overview_Status { adminive::Status_Value state{"running", "#16a34a"}; int workers{4}; }; struct Secret_Config { std::string secret{"token"}; std::string visible{"public"}; }; struct Duplicate_Config { int first{}; int second{}; }; struct Invalid_Sort_Config { int value{}; }; } namespace adminive { template <> struct Boost_Pfr_Name_Adapter { template static constexpr std::string_view name() noexcept { static_assert(Index == 0); return "alpha"; } }; template <> struct Boost_Pfr_Name_Adapter { template static constexpr std::string_view name() noexcept { static_assert(Index == 0); return "beta"; } }; template <> struct Object_Adapter { using model_type = advanced_test::Runtime_Model; static model_type snapshot(const advanced_test::Runtime_Config& value) { model_type result; result.nested.count = value.count(); result.name = value.name(); return result; } static void commit(advanced_test::Runtime_Config& target, model_type value) { target.apply(value); } }; template <> struct Reflection_Adapter : Boost_Pfr_Base_Reflection_Adapter {}; template struct Value_Adapter { using value_type = std::string; static const std::string& read(const advanced_test::Color_Code& value) noexcept { return value.value; } static void write(advanced_test::Color_Code& target, std::string value) { target.value = std::move(value); } }; template <> struct Control_Adapter { static advanced_test::Json make_control(const advanced_test::Json& field, Control_Context context) { advanced_test::Json result; result["type"] = "input-color"; result["name"] = make_field_name(context.prefix, field.at("name").get()); result["label"] = field.at("presentation").at("label"); result["clearable"] = true; return result; } static advanced_test::Json make_column(const advanced_test::Json& field) { return advanced_test::Json{{"type", "tpl"}, {"name", field.at("name")}, {"label", field.at("presentation").at("label")}, {"tpl", "${color}"}}; } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Nested_Config; return object("nested", "Nested", ADMINIVE_FIELD(T, count).editable()).validator([](const T& value) { if(value.count < 1) { throw Field_Validation_Error("count", "count must be positive"); } }); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Runtime_Model; return object("runtime", "Runtime", ADMINIVE_FIELD(T, nested).editable(), ADMINIVE_FIELD(T, name).editable()); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Inherited_Config; return reflected_object_with("inherited", "Inherited", advanced_test::Editable_Reflected_Fields{}); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Control_Config; return object("control", "Control", ADMINIVE_FIELD(T, color).editable().label("颜色")); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Serial_Device; return object("serial", "Serial", ADMINIVE_FIELD(T, port).editable().creatable(), ADMINIVE_FIELD(T, baud_rate).editable().creatable(), ADMINIVE_FIELD(T, color).editable().creatable()); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Network_Device; return object("network", "Network", ADMINIVE_FIELD(T, host).editable().creatable(), ADMINIVE_FIELD(T, port).editable().creatable()); } }; template <> struct Polymorphic_Adapter { static constexpr std::string_view discriminator() noexcept { return "type"; } static constexpr std::string_view discriminator_label() noexcept { return "设备类型"; } static auto variants() { return std::tuple( polymorphic_variant("serial", "串口设备"), polymorphic_variant("network", "网络设备") ); } static advanced_test::Json encode(const advanced_test::Device& value) { return std::visit([](const auto& device) { advanced_test::Json result = to_json(device); using T = std::remove_cvref_t; result["type"] = std::same_as ? "serial" : "network"; return result; }, value); } static void decode(advanced_test::Device& target, const advanced_test::Json& value, Write_Context context) { const std::string type = value.at("type").get(); if(type == "serial") { decode_polymorphic_alternative(target, value, context); return; } if(type == "network") { decode_polymorphic_alternative(target, value, context); return; } throw Field_Validation_Error("type", "unknown device type"); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Device_Config; return object("devices", "Devices", ADMINIVE_FIELD(T, device).editable()); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Overview_Status; return object("overview", "运行状态", ADMINIVE_FIELD(T, state).label("服务状态"), ADMINIVE_FIELD(T, workers).label("工作线程")); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Secret_Config; return object("secret", "Secret", ADMINIVE_FIELD(T, secret).sensitive().editable(), ADMINIVE_FIELD(T, visible).editable()); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Duplicate_Config; return object("duplicate", "Duplicate", field<&T::first>("same"), field<&T::second>("same")); } }; template <> struct Type_Descriptor { static auto get() { using T = advanced_test::Invalid_Sort_Config; return object("invalid_sort", "Invalid Sort", ADMINIVE_FIELD(T, value)); } }; template <> struct Type_View_Descriptor { static Table_View table() { using T = advanced_test::Invalid_Sort_Config; return table_view(column<&T::value>()).default_sort("value"); } }; } int main() { using namespace advanced_test; Runtime_Config runtime; int transaction_count{}; adminive::Resource_Transaction transaction; transaction.prepare = [](const Runtime_Model& candidate, const adminive::Request_Context&) { if(candidate.name == "blocked") { throw adminive::Field_Validation_Error("name", "name is blocked"); } }; transaction.commit = [&transaction_count](const Runtime_Model&, const adminive::Request_Context&) { ++transaction_count; }; adminive::Resource_Service service(runtime, "/config", std::move(transaction)); const auto invalid = service.update_response(R"({"nested":{"count":0}})"); ADMINIVE_CHECK(invalid.status == 422); ADMINIVE_CHECK(invalid.body.at("field_errors").contains("nested.count")); const auto rejected_commit = service.update_response(R"({"name":"blocked"})"); ADMINIVE_CHECK(rejected_commit.status == 422); ADMINIVE_CHECK(rejected_commit.body.at("field_errors").at("name") == "name is blocked"); ADMINIVE_CHECK(runtime.name() == "default"); const auto valid = service.update_response(R"({"nested":{"count":8},"name":"updated"})"); ADMINIVE_CHECK(valid.status == 200); ADMINIVE_CHECK(runtime.count() == 8); ADMINIVE_CHECK(runtime.name() == "updated"); ADMINIVE_CHECK(runtime.apply_count() == 1); ADMINIVE_CHECK(transaction_count == 1); const auto inherited = adminive::to_descriptor_json(); ADMINIVE_CHECK(inherited.at("fields").at(0).at("name") == "alpha"); ADMINIVE_CHECK(inherited.at("fields").at(1).at("name") == "beta"); adminive::Managed_Value managed_inherited; managed_inherited.field<0>().write([](int& value) { value = 12; }); managed_inherited.field<1>().write([](std::string& value) { value = "changed"; }); ADMINIVE_CHECK(managed_inherited.field<0>().snapshot() == 12); ADMINIVE_CHECK(managed_inherited.field<1>().snapshot() == "changed"); Control_Config controls; const auto control_form = adminive::to_amis_form_schema(controls, adminive::describe_edit_view()); ADMINIVE_CHECK(control_form.at("body").at(0).at("type") == "input-color"); Device_Config devices; const auto device_form = adminive::to_amis_form_schema(devices, adminive::describe_edit_view()); ADMINIVE_CHECK(device_form.at("body").at(0).at("type") == "fieldset"); ADMINIVE_CHECK(device_form.at("body").at(0).at("body").at(0).at("type") == "select"); ADMINIVE_CHECK(device_form.at("body").at(0).at("body").at(1).at("body").at(2).at("type") == "input-color"); const auto polymorphic_patch = Json{{"device", Json{{"type", "network"}, {"host", "10.0.0.1"}, {"port", 7000}, {"baud_rate", 9600}}}}; const auto polymorphic_update = adminive::apply_frontend_patch(devices, polymorphic_patch); ADMINIVE_CHECK(polymorphic_update.success); ADMINIVE_CHECK(std::holds_alternative(devices.device)); ADMINIVE_CHECK(std::get(devices.device).host == "10.0.0.1"); const auto status_descriptor = adminive::to_status_descriptor_json(); const auto status_service = adminive::make_amis_status_service(status_descriptor, "/status", 1000); ADMINIVE_CHECK(status_service.at("body").at("title") == "运行状态"); ADMINIVE_CHECK(status_service.at("body").at("body").at("body").size() == 2); const auto descriptor_without_defaults = adminive::to_descriptor_json(); ADMINIVE_CHECK(!descriptor_without_defaults.at("fields").at(0).contains("default")); const auto descriptor_with_defaults = adminive::to_descriptor_json_with_defaults(); ADMINIVE_CHECK(!descriptor_with_defaults.at("fields").at(0).contains("default")); ADMINIVE_CHECK(descriptor_with_defaults.at("fields").at(1).at("default") == "public"); Secret_Config secret; const auto frontend_secret = adminive::to_frontend_json(secret); ADMINIVE_CHECK(!frontend_secret.contains("secret")); ADMINIVE_CHECK(frontend_secret.at("visible") == "public"); bool duplicate_rejected{}; try { static_cast(adminive::to_descriptor_json()); } catch(const std::invalid_argument&) { duplicate_rejected = true; } ADMINIVE_CHECK(duplicate_rejected); bool invalid_sort_rejected{}; try { adminive::validate_table_view(adminive::describe_table_view()); } catch(const std::invalid_argument&) { invalid_sort_rejected = true; } ADMINIVE_CHECK(invalid_sort_rejected); return 0; }