userver: samples/postgres_service/postgres_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/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);
}