userver: samples/redis_service/redis_service.cpp
Loading...
Searching...
No Matches
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);
}