10#include <userver/clients/http/client.hpp>
11#include <userver/components/component_base.hpp>
12#include <userver/components/component_list.hpp>
13#include <userver/formats/json.hpp>
14#include <userver/http/content_type.hpp>
15#include <userver/server/http/http_request.hpp>
16#include <userver/server/request/request_context.hpp>
18#include <userver/storages/postgres/cluster.hpp>
20USERVER_NAMESPACE_BEGIN
29 static constexpr std::string_view kName =
"easy-dependencies";
31 virtual ~DependenciesBase();
43template <
class Dependencies>
50 DependenciesComponent(
const components::ComponentConfig& config,
const components::ComponentContext& context)
51 : DependenciesBase(config, context), dependencies_(context) {}
53 Dependencies GetDependencies()
const {
return dependencies_; }
56 Dependencies dependencies_;
63 using Callback = std::function<std::string(
const server::
http::HttpRequest&,
const impl::DependenciesBase&)>;
69 void Route(std::string_view path, Callback&& func, std::initializer_list<server::http::HttpMethod> methods);
72 template <
class Component>
74 if (component_list_.Contains(name)) {
78 component_list_.Append<Component>(name);
79 AddComponentConfig(name, config);
83 template <
class Component>
84 bool TryAddComponent(std::string_view name) {
85 if (component_list_.Contains(name)) {
89 component_list_.Append<Component>(name);
94 void DbSchema(std::string_view schema);
100 void Port(std::uint16_t port);
107 friend class HttpWith;
109 void AddComponentConfig(std::string_view name, std::string_view config);
111 HttpBase(
int argc,
const char*
const argv[]);
117 const char*
const* argv_;
118 std::string static_config_;
121 std::uint16_t port_ = 8080;
128template <
class... Dependency>
129class Dependencies
final :
public Dependency... {
131 explicit Dependencies(
const components::ComponentContext& context) : Dependency{context}... {}
133 static void RegisterOn(HttpBase& app) { (Dependency::RegisterOn(app), ...); }
140template <
class Dependency = Dependencies<>>
141class HttpWith
final {
153 class Callback
final {
155 template <
class Function>
156 Callback(Function func);
158 HttpBase::Callback Extract() &&
noexcept {
return std::move(func_); }
161 static Dependency GetDependencies(
const impl::DependenciesBase& deps) {
162 return static_cast<
const DependenciesComponent&>(deps).GetDependencies();
164 HttpBase::Callback func_;
167 HttpWith(
int argc,
const char*
const argv[]) : impl_(argc, argv) {
168 impl_.TryAddComponent<DependenciesComponent>(DependenciesComponent::kName);
170 ~HttpWith() { Dependency::RegisterOn(impl_); }
174 return (impl_.DefaultContentType(content_type), *
this);
179 std::string_view path,
181 std::initializer_list<server::http::HttpMethod> methods =
190 impl_.Route(path, std::move(func).Extract(), methods);
195 HttpWith&
Get(std::string_view path, Callback&& func) {
196 impl_.Route(path, std::move(func).Extract(), {server::http::HttpMethod::kGet});
201 HttpWith&
Post(std::string_view path, Callback&& func) {
202 impl_.Route(path, std::move(func).Extract(), {server::http::HttpMethod::kPost});
207 HttpWith&
Del(std::string_view path, Callback&& func) {
208 impl_.Route(path, std::move(func).Extract(), {server::http::HttpMethod::kDelete});
213 HttpWith&
Put(std::string_view path, Callback&& func) {
214 impl_.Route(path, std::move(func).Extract(), {server::http::HttpMethod::kPut});
219 HttpWith&
Patch(std::string_view path, Callback&& func) {
220 impl_.Route(path, std::move(func).Extract(), {server::http::HttpMethod::kPatch});
226 impl_.DbSchema(schema);
231 HttpWith&
Port(std::uint16_t port) {
238 impl_.LogLevel(level);
248template <
class Function>
249HttpWith<Dependency>::Callback::Callback(Function func) {
250 using server::
http::HttpRequest;
252 constexpr unsigned kMatches =
255 (std::is_invocable_r_v<
formats::
json::Value, Function,
const HttpRequest&,
const Dependency&> << 2) |
256 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&,
const Dependency&> << 3) |
257 (std::is_invocable_r_v<
formats::
json::Value, Function,
const HttpRequest&> << 4) |
258 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&> << 5);
261 "Failed to find a matching signature. See the easy::HttpWith::Callback docs for info on "
262 "supported signatures"
264 constexpr bool has_single_match = ((kMatches & (kMatches - 1)) == 0);
267 "Found more than one matching signature, probably due to `auto` usage in parameters. See "
268 "the easy::HttpWith::Callback docs for info on supported signatures"
271 if constexpr (kMatches & 1) {
272 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
273 req.GetHttpResponse().SetContentType(http::content_type::kApplicationJson);
274 return formats::json::ToString(f(formats::json::FromString(req.RequestBody()), GetDependencies(deps)));
276 }
else if constexpr (kMatches & 2) {
277 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
278 req.GetHttpResponse().SetContentType(http::content_type::kApplicationJson);
279 return formats::json::ToString(f(formats::json::FromString(req.RequestBody())));
281 }
else if constexpr (kMatches & 4) {
282 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
283 req.GetHttpResponse().SetContentType(http::content_type::kApplicationJson);
284 return formats::json::ToString(f(req, GetDependencies(deps)));
286 }
else if constexpr (kMatches & 8) {
287 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
288 return f(req, GetDependencies(deps));
290 }
else if constexpr (kMatches & 16) {
291 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
292 req.GetHttpResponse().SetContentType(http::content_type::kApplicationJson);
293 return formats::json::ToString(f(req));
296 static_assert(kMatches & 32);
297 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
return f(req); };
304 explicit PgDep(
const components::ComponentContext& context);
305 storages::
postgres::
Cluster& pg()
const noexcept {
return *pg_cluster_; }
306 static void RegisterOn(HttpBase& app);
309 storages::
postgres::ClusterPtr pg_cluster_;
315 explicit HttpDep(
const components::ComponentContext& context);
316 clients::http::Client& http() {
return http_; }
317 static void RegisterOn(HttpBase& app);
320 clients::http::Client& http_;
325template <
class Dependencies>
326inline constexpr auto components::kConfigFileMode<easy::DependenciesComponent<Dependencies>> =