diff --git a/_codeql_detected_source_root b/_codeql_detected_source_root new file mode 120000 index 000000000..945c9b46d --- /dev/null +++ b/_codeql_detected_source_root @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/src/mtconnect/sink/rest_sink/rest_service.cpp b/src/mtconnect/sink/rest_sink/rest_service.cpp index 1f01a8445..596e83b90 100644 --- a/src/mtconnect/sink/rest_sink/rest_service.cpp +++ b/src/mtconnect/sink/rest_sink/rest_service.cpp @@ -19,6 +19,8 @@ #include +#include + #include "error.hpp" #include "mtconnect/configuration/config_options.hpp" #include "mtconnect/entity/xml_parser.hpp" @@ -118,6 +120,7 @@ namespace mtconnect { createAssetRoutings(); createProbeRoutings(); createPutObservationRoutings(); + createConfigRoutings(); createFileRoutings(); m_server->addCommands(); @@ -435,6 +438,77 @@ namespace mtconnect { session->writeResponse(std::move(response)); } + void RestService::createConfigRoutings() + { + using namespace rest_sink; + using json = nlohmann::json; + + auto handler = [this](SessionPtr session, const RequestPtr request) -> bool { + // Build JSON response with agent configuration + json config; + + for (const auto& [key, value] : m_options) + { + // Handle different variant types + std::visit([&config, &key](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + config[key] = nullptr; + } + else if constexpr (std::is_same_v) + { + config[key] = arg; + } + else if constexpr (std::is_same_v) + { + config[key] = arg; + } + else if constexpr (std::is_same_v) + { + config[key] = arg; + } + else if constexpr (std::is_same_v) + { + config[key] = arg; + } + else if constexpr (std::is_same_v) + { + config[key] = arg.count(); + } + else if constexpr (std::is_same_v) + { + config[key] = arg.count(); + } + else if constexpr (std::is_same_v) + { + config[key] = arg; + } + else + { + // If compilation fails here, a new type has been added to ConfigOption + // and needs to be handled above + []() + { + static_assert(flag, "Unhandled type in ConfigOption variant"); + }(); + } + }, value); + } + + ResponsePtr response = make_unique( + rest_sink::status::ok, config.dump(), "application/json"); + respond(session, std::move(response), request->m_requestId); + return true; + }; + + m_server + ->addRouting({boost::beast::http::verb::get, "/config", handler}) + .document("Agent configuration request", + "Returns the current agent configuration as JSON") + .command("config"); + } + void RestService::createFileRoutings() { using namespace rest_sink; diff --git a/src/mtconnect/sink/rest_sink/rest_service.hpp b/src/mtconnect/sink/rest_sink/rest_service.hpp index 17437f2fb..5c317bf89 100644 --- a/src/mtconnect/sink/rest_sink/rest_service.hpp +++ b/src/mtconnect/sink/rest_sink/rest_service.hpp @@ -346,6 +346,8 @@ namespace mtconnect { void createAssetRoutings(); + void createConfigRoutings(); + // Current Data Collection std::string fetchCurrentData(const printer::Printer *printer, const FilterSetOpt &filterSet, const std::optional &at, bool pretty = false, diff --git a/test_package/agent_test.cpp b/test_package/agent_test.cpp index 5039741e1..f9b6106ea 100644 --- a/test_package/agent_test.cpp +++ b/test_package/agent_test.cpp @@ -29,6 +29,8 @@ #include #include +#include + #include "agent_test_helper.hpp" #include "mtconnect/agent.hpp" #include "mtconnect/asset/file_asset.hpp" @@ -2622,3 +2624,29 @@ TEST_F(AgentTest, should_initialize_observaton_to_initial_value_when_available) ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:PartCount", "0"); } } + +TEST_F(AgentTest, should_return_agent_configuration) +{ + m_agentTestHelper->createAgent("/samples/test_config.xml", 8, 4, "2.6", 4, false); + + // Test the /config endpoint + auto session = m_agentTestHelper->m_server->createSession(m_agentTestHelper->m_context); + auto request = make_unique("/config", boost::beast::http::verb::get); + + auto routing = m_agentTestHelper->m_server->getRouting(request->m_path, request->m_verb); + ASSERT_TRUE(routing); + + routing->m_handler(session, std::move(request)); + + // Verify response is JSON + ASSERT_EQ(session->m_mimeType, "application/json"); + ASSERT_EQ(session->m_code, boost::beast::http::status::ok); + + // Parse JSON response + auto json = nlohmann::json::parse(session->m_body); + + // Verify key configuration values are present + ASSERT_TRUE(json.contains("Port")); + ASSERT_TRUE(json.contains("BufferSize")); + ASSERT_TRUE(json.contains("SchemaVersion")); +}