userver: samples/redis_service/redis_service.cpp
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
samples/redis_service/redis_service.cpp
#include <userver/utest/using_namespace_userver.hpp> // IWYU pragma: keep
#include <fmt/format.h>
#include <string>
#include <string_view>
#include <userver/storages/secdist/provider_component.hpp>
namespace samples::redis {
class EvalSha final : public server::handlers::HttpHandlerBase {
public:
static constexpr std::string_view kName = "handler-script";
EvalSha(const components::ComponentConfig& config,
std::string HandleRequestThrow(
const server::http::HttpRequest& request,
private:
std::string EvalShaRequest(const server::http::HttpRequest& request) const;
std::string ScriptLoad(const server::http::HttpRequest& request) const;
storages::redis::ClientPtr redis_client_;
storages::redis::CommandControl redis_cc_;
};
class KeyValue final : public server::handlers::HttpHandlerBase {
public:
static constexpr std::string_view kName = "handler-key-value";
KeyValue(const components::ComponentConfig& config,
std::string HandleRequestThrow(
const server::http::HttpRequest& request,
private:
std::string GetValue(std::string_view key,
const server::http::HttpRequest& request) const;
std::string PostValue(std::string_view key,
const server::http::HttpRequest& request) const;
std::string DeleteValue(std::string_view key) const;
storages::redis::ClientPtr redis_client_;
storages::redis::CommandControl redis_cc_;
};
} // namespace samples::redis
namespace samples::redis {
KeyValue::KeyValue(const components::ComponentConfig& config,
: server::handlers::HttpHandlerBase(config, context),
redis_client_{
context.FindComponent<components::Redis>("key-value-database")
.GetClient("taxi-tmp")},
redis_cc_{std::chrono::seconds{15}, std::chrono::seconds{60}, 4} {}
std::string KeyValue::HandleRequestThrow(
const server::http::HttpRequest& request,
server::request::RequestContext& /*context*/) const {
const auto& key = request.GetArg("key");
if (key.empty()) {
server::handlers::ExternalBody{"No 'key' query argument"});
}
switch (request.GetMethod()) {
case server::http::HttpMethod::kGet:
return GetValue(key, request);
case server::http::HttpMethod::kPost:
return PostValue(key, request);
case server::http::HttpMethod::kDelete:
return DeleteValue(key);
default:
fmt::format("Unsupported method {}", request.GetMethod())});
}
}
std::string KeyValue::GetValue(std::string_view key,
const server::http::HttpRequest& request) const {
const auto result = redis_client_->Get(std::string{key}, redis_cc_).Get();
if (!result) {
request.SetResponseStatus(server::http::HttpStatus::kNotFound);
return {};
}
return *result;
}
std::string KeyValue::PostValue(
std::string_view key, const server::http::HttpRequest& request) const {
const auto& value = request.GetArg("value");
const auto result =
redis_client_->SetIfNotExist(std::string{key}, value, redis_cc_).Get();
if (!result) {
request.SetResponseStatus(server::http::HttpStatus::kConflict);
return {};
}
request.SetResponseStatus(server::http::HttpStatus::kCreated);
return std::string{value};
}
std::string KeyValue::DeleteValue(std::string_view key) const {
const auto result = redis_client_->Del(std::string{key}, redis_cc_).Get();
return std::to_string(result);
}
EvalSha::EvalSha(const components::ComponentConfig& config,
: server::handlers::HttpHandlerBase(config, context),
redis_client_{
context.FindComponent<components::Redis>("key-value-database")
.GetClient("taxi-tmp")} {}
std::string EvalSha::HandleRequestThrow(
const server::http::HttpRequest& request,
const auto& command = request.GetArg("command");
if (command.empty()) {
server::handlers::ExternalBody{"No 'command' query argument"});
}
if (command == "evalsha") {
return EvalShaRequest(request);
}
if (command == "scriptload") {
return ScriptLoad(request);
}
"Invalid 'command' query argument: must be 'evalsha' or 'scriptload'"});
}
std::string EvalSha::EvalShaRequest(
const server::http::HttpRequest& request) const {
const auto& script_hash = request.GetArg("hash");
if (script_hash.empty()) {
server::handlers::ExternalBody{"No 'hash' query argument"});
}
const auto& key = request.GetArg("key");
if (key.empty()) {
server::handlers::ExternalBody{"No 'key' query argument"});
}
auto redis_request =
redis_client_->EvalSha<std::string>(script_hash, {key}, {}, redis_cc_);
const auto eval_sha_result = redis_request.Get();
if (eval_sha_result.HasValue()) {
return eval_sha_result.Get();
}
if (eval_sha_result.IsNoScriptError()) {
return "NOSCRIPT";
}
server::handlers::ExternalBody{"Some internal redis error"});
}
std::string EvalSha::ScriptLoad(
const server::http::HttpRequest& request) const {
const auto& script = request.GetArg("script");
if (script.empty()) {
server::handlers::ExternalBody{"No 'script' query argument"});
}
const auto& key = request.GetArg("key");
if (key.empty()) {
server::handlers::ExternalBody{"No 'key' query argument"});
}
const auto shard = redis_client_->ShardByKey(key);
auto redis_request = redis_client_->ScriptLoad(script, shard, redis_cc_);
return redis_request.Get();
}
} // namespace samples::redis
int main(int argc, char* argv[]) {
const auto component_list =
.Append<samples::redis::KeyValue>()
.Append<samples::redis::EvalSha>()
.Append<components::DefaultSecdistProvider>()
.Append<components::Redis>("key-value-database")
.Append<components::TestsuiteSupport>()
return utils::DaemonMain(argc, argv, component_list);
}