userver: samples/postgres_service/postgres_service.cpp
Loading...
Searching...
No Matches
samples/postgres_service/postgres_service.cpp
namespace samples::pg {
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;
};
} // namespace samples::pg
namespace samples::pg {
KeyValue::KeyValue(const components::ComponentConfig& config,
: HttpHandlerBase(config, context),
pg_cluster_(
context.FindComponent<components::Postgres>("key-value-database")
.GetCluster()) {
constexpr auto kCreateTable = R"~(
CREATE TABLE IF NOT EXISTS key_value_table (
key VARCHAR PRIMARY KEY,
value VARCHAR
)
)~";
pg_cluster_->Execute(ClusterHostType::kMaster, kCreateTable);
}
std::string KeyValue::HandleRequestThrow(
const server::http::HttpRequest& request,
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())});
}
}
const storages::postgres::Query kSelectValue{
"SELECT value FROM key_value_table WHERE key=$1",
storages::postgres::Query::Name{"sample_select_value"},
};
std::string KeyValue::GetValue(std::string_view key,
const server::http::HttpRequest& request) const {
storages::postgres::ResultSet res = pg_cluster_->Execute(
if (res.IsEmpty()) {
request.SetResponseStatus(server::http::HttpStatus::kNotFound);
return {};
}
return res.AsSingleRow<std::string>();
}
const storages::postgres::Query kInsertValue{
"INSERT INTO key_value_table (key, value) "
"VALUES ($1, $2) "
"ON CONFLICT DO NOTHING",
storages::postgres::Query::Name{"sample_insert_value"},
};
std::string KeyValue::PostValue(
std::string_view key, const server::http::HttpRequest& request) const {
const auto& value = request.GetArg("value");
pg_cluster_->Begin("sample_transaction_insert_key_value",
auto res = transaction.Execute(kInsertValue, key, value);
if (res.RowsAffected()) {
transaction.Commit();
request.SetResponseStatus(server::http::HttpStatus::kCreated);
return std::string{value};
}
res = transaction.Execute(kSelectValue, key);
transaction.Rollback();
auto result = res.AsSingleRow<std::string>();
if (result != value) {
request.SetResponseStatus(server::http::HttpStatus::kConflict);
}
return res.AsSingleRow<std::string>();
}
std::string KeyValue::DeleteValue(std::string_view key) const {
auto res =
"DELETE FROM key_value_table WHERE key=$1", key);
return std::to_string(res.RowsAffected());
}
} // namespace samples::pg
int main(int argc, char* argv[]) {
const auto component_list =
.Append<samples::pg::KeyValue>()
.Append<components::Postgres>("key-value-database")
.Append<clients::dns::Component>();
return utils::DaemonMain(argc, argv, component_list);
}