Archetype: cpp_nats_handler.hpp.mustache

Table of Contents

Subject handler skeleton dispatching protocol messages to the entity service. nats-handler profile.

See the Template variable reference for the complete list of available variables and their semantics.

Template

The full template source. Edit here and re-tangle with compass build --direct tangle_codegen_templates to regenerate library/templates/cpp_nats_handler.hpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/cpp_messaging.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#ifndef ORES_{{component_core_upper}}_MESSAGING_{{entity_singular_upper}}_HANDLER_HPP
#define ORES_{{component_core_upper}}_MESSAGING_{{entity_singular_upper}}_HANDLER_HPP

#include <optional>
#include "ores.logging/make_logger.hpp"
#include "ores.nats/domain/message.hpp"
#include "ores.nats/service/client.hpp"
#include "ores.database/domain/context.hpp"
#include "ores.security/jwt/jwt_authenticator.hpp"
#include "ores.service/messaging/handler_helpers.hpp"
#include "ores.service/service/request_context.hpp"
#include "ores.{{component_include}}/messaging/{{entity_singular}}_protocol.hpp"
#include "ores.{{component_core}}/service/{{entity_singular}}_service.hpp"
{{#system_tenant_validation}}
#include "ores.utility/uuid/tenant_id.hpp"
{{/system_tenant_validation}}

namespace ores::{{component}}::messaging {

namespace {
inline auto& {{entity_singular}}_handler_lg() {
    static auto instance = ores::logging::make_logger(
        "ores.{{component}}.messaging.{{entity_singular}}_handler");
    return instance;
}
} // namespace

using ores::service::messaging::reply;
using ores::service::messaging::decode;
using ores::service::messaging::error_reply;
using ores::service::messaging::has_permission;
using namespace ores::logging;

/**
 * @brief NATS message handler for {{entity_singular_words}} operations.
{{#system_tenant_validation}}
 *
 * {{entity_plural_words_cap}} are system-owned global entities; list and history
 * operations use the system tenant context.
{{/system_tenant_validation}}
 */
class {{entity_singular}}_handler {
public:
    {{entity_singular}}_handler(ores::nats::service::client& nats,
        ores::database::context ctx,
        std::optional<ores::security::jwt::jwt_authenticator> verifier)
        : nats_(nats), ctx_(std::move(ctx)), verifier_(std::move(verifier)) {}

    void list(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
{{#system_tenant_validation}}
        const auto sys_ctx = req_ctx.with_tenant(
            ores::utility::uuid::tenant_id::system(), req_ctx.actor());
        service::{{entity_singular}}_service svc(sys_ctx);
{{/system_tenant_validation}}
{{^system_tenant_validation}}
        service::{{entity_singular}}_service svc(req_ctx);
{{/system_tenant_validation}}
        get_{{entity_plural}}_response resp;
{{#list_filter_column}}
        if (auto req = decode<get_{{entity_plural}}_request>(msg)) {
            try {
                resp.{{entity_plural_short}} = svc.list_{{entity_plural_short}}(req->{{list_filter_column}});
                resp.total_available_count =
                    static_cast<int>(resp.{{entity_plural_short}}.size());
                resp.success = true;
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                resp.success = false;
                resp.message = e.what();
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
            return;
        }
{{/list_filter_column}}
{{^list_filter_column}}
        if (auto req = decode<get_{{entity_plural}}_request>(msg)) {
            try {
                resp.{{entity_plural_short}} = svc.list_{{entity_plural_short}}(req->offset, req->limit);
                resp.total_available_count = static_cast<int>(svc.count_{{entity_plural_short}}());
                resp.success = true;
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                resp.success = false;
                resp.message = e.what();
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
            return;
        }
{{/list_filter_column}}
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Completed " << msg.subject;
        reply(nats_, msg, resp);
    }

    void save(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
        if (!has_permission(req_ctx, "{{component}}::{{entity_plural}}:write")) {
            error_reply(nats_, msg, ores::service::error_code::forbidden);
            return;
        }
        service::{{entity_singular}}_service svc(req_ctx);
        if (auto req = decode<save_{{entity_singular}}_request>(msg)) {
            try {
                svc.save_{{entity_singular_short}}(req->data);
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
                    << "Completed " << msg.subject;
                reply(nats_, msg,
                    save_{{entity_singular}}_response{.success = true});
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                reply(nats_, msg, save_{{entity_singular}}_response{
                    .success = false, .message = e.what()});
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
        }
    }

    void history(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
{{#system_tenant_validation}}
        const auto sys_ctx = req_ctx.with_tenant(
            ores::utility::uuid::tenant_id::system(), req_ctx.actor());
        service::{{entity_singular}}_service svc(sys_ctx);
{{/system_tenant_validation}}
{{^system_tenant_validation}}
        service::{{entity_singular}}_service svc(req_ctx);
{{/system_tenant_validation}}
        if (auto req = decode<get_{{entity_singular}}_history_request>(msg)) {
            try {
                auto hist = svc.get_{{entity_singular_short}}_history(req->{{history_request_id_field}});
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
                    << "Completed " << msg.subject;
                reply(nats_, msg, get_{{entity_singular}}_history_response{
                    .history = std::move(hist), .success = true});
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                reply(nats_, msg, get_{{entity_singular}}_history_response{
                    .success = false, .message = e.what()});
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
        }
    }

    void remove(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
        if (!has_permission(req_ctx, "{{component}}::{{entity_plural}}:delete")) {
            error_reply(nats_, msg, ores::service::error_code::forbidden);
            return;
        }
        service::{{entity_singular}}_service svc(req_ctx);
        if (auto req = decode<delete_{{entity_singular}}_request>(msg)) {
            try {
{{#single_delete}}
                svc.delete_{{entity_singular_short}}(req->{{delete_request_id_field}});
{{/single_delete}}
{{^single_delete}}
                svc.delete_{{entity_plural_short}}(req->{{delete_request_id_field}});
{{/single_delete}}
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
                    << "Completed " << msg.subject;
                reply(nats_, msg,
                    delete_{{entity_singular}}_response{.success = true});
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                reply(nats_, msg, delete_{{entity_singular}}_response{
                    .success = false, .message = e.what()});
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
        }
    }

{{#extra_list_requests}}
    void list_{{name_suffix}}(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
        service::{{entity_singular}}_service svc(req_ctx);
        get_{{entity_plural}}_{{name_suffix}}_response resp;
        try {
            if (auto req = decode<get_{{entity_plural}}_{{name_suffix}}_request>(msg)) {
                resp.{{entity_plural_short}} = svc.{{service_method}}(req->{{filter_column}});
                resp.success = true;
            }
        } catch (const std::exception& e) {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                << msg.subject << " failed: " << e.what();
            resp.success = false;
            resp.message = e.what();
        }
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Completed " << msg.subject;
        reply(nats_, msg, resp);
    }

{{/extra_list_requests}}
private:
    ores::nats::service::client& nats_;
    ores::database::context ctx_;
    std::optional<ores::security::jwt::jwt_authenticator> verifier_;
};

} // namespace ores::{{component}}::messaging

#endif
{{/domain_entity}}
{{#entity}}
#ifndef ORES_{{component_core_upper}}_MESSAGING_{{entity_singular_upper}}_HANDLER_HPP
#define ORES_{{component_core_upper}}_MESSAGING_{{entity_singular_upper}}_HANDLER_HPP

#include <optional>
#include "ores.logging/make_logger.hpp"
#include "ores.nats/domain/message.hpp"
#include "ores.nats/service/client.hpp"
#include "ores.database/domain/context.hpp"
#include "ores.security/jwt/jwt_authenticator.hpp"
#include "ores.service/messaging/handler_helpers.hpp"
#include "ores.service/service/request_context.hpp"
#include "ores.{{component_include}}/messaging/{{entity_singular}}_protocol.hpp"
#include "ores.{{component_core}}/service/{{entity_singular}}_service.hpp"
{{#system_tenant_validation}}
#include "ores.utility/uuid/tenant_id.hpp"
{{/system_tenant_validation}}

namespace ores::{{component}}::messaging {

namespace {
inline auto& {{entity_singular}}_handler_lg() {
    static auto instance = ores::logging::make_logger(
        "ores.{{component}}.messaging.{{entity_singular}}_handler");
    return instance;
}
} // namespace

using ores::service::messaging::reply;
using ores::service::messaging::decode;
using ores::service::messaging::error_reply;
using ores::service::messaging::has_permission;
using namespace ores::logging;

/**
 * @brief NATS message handler for {{entity_singular_words}} operations.
{{#system_tenant_validation}}
 *
 * {{entity_plural_words_cap}} are system-owned global entities; list and history
 * operations use the system tenant context.
{{/system_tenant_validation}}
 */
class {{entity_singular}}_handler {
public:
    {{entity_singular}}_handler(ores::nats::service::client& nats,
        ores::database::context ctx,
        std::optional<ores::security::jwt::jwt_authenticator> verifier)
        : nats_(nats), ctx_(std::move(ctx)), verifier_(std::move(verifier)) {}

    void list(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
{{#system_tenant_validation}}
        const auto sys_ctx = req_ctx.with_tenant(
            ores::utility::uuid::tenant_id::system(), req_ctx.actor());
        service::{{entity_singular}}_service svc(sys_ctx);
{{/system_tenant_validation}}
{{^system_tenant_validation}}
        service::{{entity_singular}}_service svc(req_ctx);
{{/system_tenant_validation}}
        get_{{entity_plural}}_response resp;
        if (auto req = decode<get_{{entity_plural}}_request>(msg)) {
            try {
                resp.{{entity_plural_short}} = svc.list_{{entity_plural_short}}(req->offset, req->limit);
                resp.total_available_count = static_cast<int>(svc.count_{{entity_plural_short}}());
                resp.success = true;
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                resp.success = false;
                resp.message = e.what();
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
            return;
        }
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Completed " << msg.subject;
        reply(nats_, msg, resp);
    }

    void save(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
        if (!has_permission(req_ctx, "{{component}}::{{entity_plural}}:write")) {
            error_reply(nats_, msg, ores::service::error_code::forbidden);
            return;
        }
        service::{{entity_singular}}_service svc(req_ctx);
        if (auto req = decode<save_{{entity_singular}}_request>(msg)) {
            try {
                svc.save_{{entity_singular_short}}(req->data);
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
                    << "Completed " << msg.subject;
                reply(nats_, msg,
                    save_{{entity_singular}}_response{.success = true});
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                reply(nats_, msg, save_{{entity_singular}}_response{
                    .success = false, .message = e.what()});
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
        }
    }

    void history(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
{{#system_tenant_validation}}
        const auto sys_ctx = req_ctx.with_tenant(
            ores::utility::uuid::tenant_id::system(), req_ctx.actor());
        service::{{entity_singular}}_service svc(sys_ctx);
{{/system_tenant_validation}}
{{^system_tenant_validation}}
        service::{{entity_singular}}_service svc(req_ctx);
{{/system_tenant_validation}}
        if (auto req = decode<get_{{entity_singular}}_history_request>(msg)) {
            try {
                auto hist = svc.get_{{entity_singular_short}}_history(req->{{history_request_id_field}});
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
                    << "Completed " << msg.subject;
                reply(nats_, msg, get_{{entity_singular}}_history_response{
                    .history = std::move(hist), .success = true});
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                reply(nats_, msg, get_{{entity_singular}}_history_response{
                    .success = false, .message = e.what()});
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
        }
    }

    void remove(ores::nats::message msg) {
        BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
            << "Handling " << msg.subject;
        auto req_ctx_expected = ores::service::service::make_request_context(
            ctx_, msg, verifier_);
        if (!req_ctx_expected) {
            error_reply(nats_, msg, req_ctx_expected.error());
            return;
        }
        const auto& req_ctx = *req_ctx_expected;
        if (!has_permission(req_ctx, "{{component}}::{{entity_plural}}:delete")) {
            error_reply(nats_, msg, ores::service::error_code::forbidden);
            return;
        }
        service::{{entity_singular}}_service svc(req_ctx);
        if (auto req = decode<delete_{{entity_singular}}_request>(msg)) {
            try {
{{#single_delete}}
                svc.delete_{{entity_singular_short}}(req->{{delete_request_id_field}});
{{/single_delete}}
{{^single_delete}}
                svc.delete_{{entity_plural_short}}(req->{{delete_request_id_field}});
{{/single_delete}}
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), debug)
                    << "Completed " << msg.subject;
                reply(nats_, msg,
                    delete_{{entity_singular}}_response{.success = true});
            } catch (const std::exception& e) {
                BOOST_LOG_SEV({{entity_singular}}_handler_lg(), error)
                    << msg.subject << " failed: " << e.what();
                reply(nats_, msg, delete_{{entity_singular}}_response{
                    .success = false, .message = e.what()});
            }
        } else {
            BOOST_LOG_SEV({{entity_singular}}_handler_lg(), warn)
                << "Failed to decode: " << msg.subject;
            error_reply(nats_, msg, ores::service::error_code::bad_request);
        }
    }

private:
    ores::nats::service::client& nats_;
    ores::database::context ctx_;
    std::optional<ores::security::jwt::jwt_authenticator> verifier_;
};

} // namespace ores::{{component}}::messaging

#endif
{{/entity}}

See also

Emacs 29.1 (Org mode 9.6.6)