|
@@ -3,6 +3,7 @@
|
|
|
#include "common/time_utils.hpp"
|
|
#include "common/time_utils.hpp"
|
|
|
#include "proto/runner.grpc.pb.h"
|
|
#include "proto/runner.grpc.pb.h"
|
|
|
#include <grpcpp/grpcpp.h>
|
|
#include <grpcpp/grpcpp.h>
|
|
|
|
|
+#include <cctype>
|
|
|
#include <optional>
|
|
#include <optional>
|
|
|
|
|
|
|
|
namespace smartbotic::webserver::api {
|
|
namespace smartbotic::webserver::api {
|
|
@@ -171,10 +172,59 @@ void WebhookController::handleWebhook(const httplib::Request& req, httplib::Resp
|
|
|
|
|
|
|
|
// Return result
|
|
// Return result
|
|
|
if (!grpc_res.result().empty()) {
|
|
if (!grpc_res.result().empty()) {
|
|
|
|
|
+ nlohmann::json parsed;
|
|
|
|
|
+ bool parsed_ok = true;
|
|
|
try {
|
|
try {
|
|
|
- auto result = nlohmann::json::parse(grpc_res.result());
|
|
|
|
|
- sendJson(res, result);
|
|
|
|
|
|
|
+ parsed = nlohmann::json::parse(grpc_res.result());
|
|
|
} catch (...) {
|
|
} catch (...) {
|
|
|
|
|
+ parsed_ok = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (parsed_ok && parsed.is_object() && parsed.contains("_webhookResponse")) {
|
|
|
|
|
+ const auto& spec = parsed["_webhookResponse"];
|
|
|
|
|
+
|
|
|
|
|
+ int status_code = spec.value("status", 200);
|
|
|
|
|
+ if (status_code < 100 || status_code > 599) {
|
|
|
|
|
+ LOG_WARN("Webhook response asked for status {}, which is not a valid HTTP status; sending 500",
|
|
|
|
|
+ status_code);
|
|
|
|
|
+ status_code = 500;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::string content_type = "application/json";
|
|
|
|
|
+ if (spec.contains("headers") && spec["headers"].is_object()) {
|
|
|
|
|
+ for (auto it = spec["headers"].begin(); it != spec["headers"].end(); ++it) {
|
|
|
|
|
+ if (!it.value().is_string()) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Content-Type reaches httplib through set_content rather
|
|
|
|
|
+ // than as a header, and setting both sends it twice.
|
|
|
|
|
+ std::string name = it.key();
|
|
|
|
|
+ std::string lowered;
|
|
|
|
|
+ for (char c : name) {
|
|
|
|
|
+ lowered += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (lowered == "content-type") {
|
|
|
|
|
+ content_type = it.value().get<std::string>();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ res.set_header(name.c_str(), it.value().get<std::string>().c_str());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::string body;
|
|
|
|
|
+ if (spec.contains("body")) {
|
|
|
|
|
+ const auto& value = spec["body"];
|
|
|
|
|
+ body = value.is_string() ? value.get<std::string>() : value.dump();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ res.status = status_code;
|
|
|
|
|
+ res.set_content(body, content_type.c_str());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (parsed_ok) {
|
|
|
|
|
+ sendJson(res, parsed);
|
|
|
|
|
+ } else {
|
|
|
res.set_content(grpc_res.result(), "text/plain");
|
|
res.set_content(grpc_res.result(), "text/plain");
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|