Skip to content

Commit

Permalink
WIP: Use ErrorType in CmdResult
Browse files Browse the repository at this point in the history
Signed-off-by: Kai-Uwe Hermann <kai-uwe.hermann@pionix.de>
  • Loading branch information
hikinggrass committed Oct 24, 2024
1 parent 6e784c9 commit 2f80665
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 15 deletions.
28 changes: 27 additions & 1 deletion include/framework/everest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,36 @@ using TelemetryEntry = std::variant<std::string, const char*, bool, int32_t, uin
using TelemetryMap = std::map<std::string, TelemetryEntry>;
using UnsubscribeToken = std::function<void()>;

enum class ErrorType {
MessageParsing,
SchemaValidation,
HandlerException,
Timeout,
Shutdown,
Unknown
};

struct ErrorMessage {
ErrorType type;

Check notice on line 46 in include/framework/everest.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/framework/everest.hpp#L46

struct member 'ErrorMessage::type' is never used.
std::string msg;

Check notice on line 47 in include/framework/everest.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/framework/everest.hpp#L47

struct member 'ErrorMessage::msg' is never used.
};

namespace conversions {
std::string error_type_to_string(ErrorType error_type);
ErrorType string_to_error_type(const std::string& error_type_string);
} // namespace conversions

void to_json(nlohmann::json& j, const ErrorMessage& e);
void from_json(const nlohmann::json& j, ErrorMessage& e);

/// \brief Result of a command
struct CmdResult {
std::optional<json> result;
std::optional<json> error; // TODO: use proper ErrorType instead of json
std::optional<ErrorMessage> error;

// CmdResult(std::optional<json> result = std::nullopt, std::optional<ErrorMessage> error = std::nullopt) :
// result(result), error(error) {
// }
};

class EverestCmdError : public Everest::EverestBaseRuntimeError {
Expand Down
91 changes: 77 additions & 14 deletions lib/everest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,54 @@ namespace Everest {
const auto remote_cmd_res_timeout_seconds = 300;
const std::array<std::string, 3> TELEMETRY_RESERVED_KEYS = {{"connector_id"}};

namespace conversions {
constexpr auto ERROR_TYPE_MESSAGE_PARSING = "MessageParsing";
constexpr auto ERROR_TYPE_SCHEMA_VALIDATION = "SchemaValidation";
constexpr auto ERROR_TYPE_HANDLER_EXCEPTION = "HandlerException";
constexpr auto ERROR_TYPE_TIMEOUT = "Timeout";
constexpr auto ERROR_TYPE_SHUTDOWN = "Shutdown";
constexpr auto ERROR_TYPE_UNKNOWN = "Unknown";
std::string error_type_to_string(ErrorType error_type) {
switch (error_type) {
case ErrorType::MessageParsing:
return ERROR_TYPE_MESSAGE_PARSING;
break;
case ErrorType::SchemaValidation:
return ERROR_TYPE_SCHEMA_VALIDATION;
break;
case ErrorType::HandlerException:
return ERROR_TYPE_HANDLER_EXCEPTION;
break;
case ErrorType::Timeout:
return ERROR_TYPE_TIMEOUT;
break;
case ErrorType::Shutdown:
return ERROR_TYPE_SHUTDOWN;
break;
case ErrorType::Unknown:
return ERROR_TYPE_UNKNOWN;
break;
}

return ERROR_TYPE_UNKNOWN;
}
ErrorType string_to_error_type(const std::string& error_type_string) {
if (error_type_string == ERROR_TYPE_MESSAGE_PARSING) {
return ErrorType::MessageParsing;
} else if (error_type_string == ERROR_TYPE_SCHEMA_VALIDATION) {
return ErrorType::SchemaValidation;
} else if (error_type_string == ERROR_TYPE_HANDLER_EXCEPTION) {
return ErrorType::HandlerException;
} else if (error_type_string == ERROR_TYPE_TIMEOUT) {
return ErrorType::Timeout;
} else if (error_type_string == ERROR_TYPE_SHUTDOWN) {
return ErrorType::Shutdown;
}

return ErrorType::Unknown;
}
} // namespace conversions

Everest::Everest(std::string module_id_, const Config& config_, bool validate_data_with_schema,
const std::string& mqtt_server_socket_path, const std::string& mqtt_server_address,
int mqtt_server_port, const std::string& mqtt_everest_prefix, const std::string& mqtt_external_prefix,
Expand Down Expand Up @@ -372,9 +420,9 @@ json Everest::call_cmd(const Requirement& req, const std::string& cmd_name, json
EVLOG_error << fmt::format(
"Received error {} for {}->{}()", data.at("error"),
this->config.printable_identifier(connection["module_id"], connection["implementation_id"]), cmd_name);
res_promise.set_value({std::nullopt, data.at("error")});
res_promise.set_value(CmdResult{std::nullopt, data.at("error")});
} else {
res_promise.set_value({std::move(data["retval"]), std::nullopt});
res_promise.set_value(CmdResult{std::move(data["retval"]), std::nullopt});
}
};

Expand Down Expand Up @@ -412,12 +460,10 @@ json Everest::call_cmd(const Requirement& req, const std::string& cmd_name, json
this->mqtt_abstraction.unregister_handler(cmd_topic, res_token);

if (result.error.has_value()) {
// throw appropriate exception
auto& error = result.error.value();
auto error_message = fmt::format("{}: {}", error.at("type"), error.at("msg"));
throw EverestBaseRuntimeError(error_message);
throw EverestCmdError(fmt::format("{}: {}", conversions::error_type_to_string(error.type), error.msg));
} else if (not result.result.has_value()) {
throw EverestBaseRuntimeError("Command did not return result");
throw EverestCmdError("Command did not return result");
} else {
return result.result.value();
}
Expand Down Expand Up @@ -862,6 +908,11 @@ void Everest::provide_cmd(const std::string impl_id, const std::string cmd_name,
this->config.printable_identifier(this->module_id, impl_id), cmd_name,
fmt::join(arg_names, ","));

json res_data = json({});
// FIXME: this id lookup might fail -> return MessageParsing error
res_data["id"] = data["id"];
std::optional<ErrorMessage> error;

// check data and ignore it if not matching (publishing it should have
// been prohibited already)
if (this->validate_data_with_schema) {
Expand All @@ -881,27 +932,26 @@ void Everest::provide_cmd(const std::string impl_id, const std::string cmd_name,
} catch (const std::exception& e) {
EVLOG_warning << fmt::format("Ignoring incoming cmd '{}' because not matching manifest schema: {}",
cmd_name, e.what());
return;
error = ErrorMessage{ErrorType::SchemaValidation, e.what()};
}
}

// publish results
json res_data = json({});
res_data["id"] = data["id"];
auto error = false;

// call real cmd handler
try {
res_data["retval"] = handler(data["args"]);
if (not error.has_value()) {
res_data["retval"] = handler(data["args"]);
}
} catch (const std::exception& e) {
EVLOG_verbose << fmt::format("Exception during handling of: {}->{}({}): {}",
this->config.printable_identifier(this->module_id, impl_id), cmd_name,
fmt::join(arg_names, ","), e.what());
res_data["error"] = {{"type", "HandlerException"}, {"msg", e.what()}};
error = ErrorMessage{ErrorType::HandlerException, e.what()};
}

// check retval agains manifest
if (not error && this->validate_data_with_schema) {
if (not error.has_value() && this->validate_data_with_schema) {
try {
// only use validator on non-null return types
if (!(res_data["retval"].is_null() &&
Expand All @@ -917,11 +967,14 @@ void Everest::provide_cmd(const std::string impl_id, const std::string cmd_name,
EVLOG_warning << fmt::format("Ignoring return value of cmd '{}' because the validation of the result "
"failed: {}\ndefinition: {}\ndata: {}",
cmd_name, e.what(), cmd_definition, res_data);
return;
error = ErrorMessage{ErrorType::SchemaValidation, e.what()};
}
}

res_data["origin"] = this->module_id;
if (error.has_value()) {
res_data["error"] = error.value();
}

json res_publish_data = json::object({{"name", cmd_name}, {"type", "result"}, {"data", res_data}});

Expand Down Expand Up @@ -1099,4 +1152,14 @@ bool Everest::check_arg(ArgumentType arg_types, json manifest_arg) {
}
return true;
}

// TODO fix these conversions
void to_json(nlohmann::json& j, const ErrorMessage& e) {
j = {{"type", conversions::error_type_to_string(e.type)}, {"msg", e.msg}};
}

void from_json(const nlohmann::json& j, ErrorMessage& e) {
e.type = conversions::string_to_error_type(j.at("type"));
e.msg = j.at("msg");
}
} // namespace Everest

0 comments on commit 2f80665

Please sign in to comment.