userver: userver/middlewares/pipeline.hpp Source File
Loading...
Searching...
No Matches
pipeline.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/middlewares/pipeline.hpp
4/// @brief Lists all available middlewares and builds their order of execution.
5
6#include <unordered_map>
7
8#include <userver/components/component_base.hpp>
9#include <userver/components/component_fwd.hpp>
10#include <userver/utils/meta_light.hpp>
11#include <userver/yaml_config/fwd.hpp>
12
13#include <userver/middlewares/impl/middleware_pipeline_config.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17/// Pipeline to configure middlewares order and static configs.
18/// @see @ref scripts/docs/en/userver/grpc/middlewares_order.md
19/// @see @ref scripts/docs/en/userver/grpc/middlewares_configuration.md
20namespace middlewares {
21
22/// @brief The dependency type between middlewares.
23///
24/// Iff dependency type from 'X' to 'Y' is `kStrong` and 'Y' is disabled, middlewares pipeline will failure when start,
25/// otherwise (`kWeak`) we ignore this dependency.
26enum class DependencyType {
27 kStrong = 0,
28 kWeak = 1,
29};
30
31namespace impl {
32
33struct Connect final {
34 std::string node_name{};
35 DependencyType type{DependencyType::kStrong};
36};
37
38struct MiddlewareDependency final {
39 std::string middleware_name{};
40 std::vector<Connect> befores{};
41 std::vector<Connect> afters{};
42 bool enabled{true};
43 std::optional<std::string> group{};
44};
45
46using Dependencies = std::unordered_map<std::string, MiddlewareDependency>;
47
48class MiddlewarePipeline final {
49public:
50 explicit MiddlewarePipeline(Dependencies&& deps);
51
52 std::vector<std::string> GetPerServiceMiddlewares(const MiddlewareRunnerConfig& config) const;
53
54 const MiddlewareOrderedList& GetOrderedList() const { return pipeline_; }
55
56private:
57 Dependencies deps_{};
58 MiddlewareOrderedList pipeline_{};
59};
60
61template <typename Group>
62std::string BeginOfGroup() {
63 return std::string{Group::kName} + "#begin";
64}
65
66template <typename Group>
67std::string EndOfGroup() {
68 return std::string{Group::kName} + "#end";
69}
70
71/// @brief A base component to create middlewares pipeline.
72///
73/// ## Static options of middlewares::impl::AnyMiddlewarePipelineComponent :
74/// @include{doc} scripts/docs/en/components_schema/core/src/middlewares/pipeline.md
75///
76/// Options inherited from @ref components::ComponentBase :
77/// @include{doc} scripts/docs/en/components_schema/core/src/components/impl/component_base.md
78///
79/// ## Config example:
80///
81/// @snippet grpc/functional_tests/middleware_server/static_config.yaml middleware pipeline component config
82
83class AnyMiddlewarePipelineComponent : public components::ComponentBase {
84public:
85 AnyMiddlewarePipelineComponent(
86 const components::ComponentConfig& config,
87 const components::ComponentContext& context,
88 impl::BuiltInConfig&& builtin_config
89 );
90
91 static yaml_config::Schema GetStaticConfigSchema();
92
93 /// @cond
94 /// Only for internal use.
95 const impl::MiddlewarePipeline& GetPipeline() const;
96 /// @endcond
97
98private:
99 impl::MiddlewarePipeline pipeline_;
100};
101
102} // namespace impl
103
104/// @brief Specifies a middleware's order with respect to other middlewares.
105///
106/// If you don't care about the order in relative to others, ignore this and your middleware
107/// will be in the `kUser` group.
108/// Otherwise, pass an instance of this class to `MiddlewareComponentBase` in the constructor of your
109/// middleware component.
110class MiddlewareDependencyBuilder final {
111public:
112 /// @brief Make an empty dependency builder. For middlewares, `InGroup` must be called at some point.
113 MiddlewareDependencyBuilder() = default;
114
115 MiddlewareDependencyBuilder(const MiddlewareDependencyBuilder&) = default;
116 MiddlewareDependencyBuilder(MiddlewareDependencyBuilder&&) noexcept = default;
117 MiddlewareDependencyBuilder& operator=(const MiddlewareDependencyBuilder&) = default;
118 MiddlewareDependencyBuilder& operator=(MiddlewareDependencyBuilder&&) = default;
119
120 /// @brief Add dependency for your middleware. Your middleware will be before `MiddlewareBefore` in the pipeline
121 /// @param type is connection type between middlewares
122 template <typename MiddlewareBefore>
123 MiddlewareDependencyBuilder Before(DependencyType type = DependencyType::kStrong) &&;
124
125 /// @brief Add dependency for your middleware. Your middleware will be before `before` in the pipeline
126 /// @param type is connection type between middlewares
127 /// @param before is the middleware component name
128 MiddlewareDependencyBuilder Before(std::string_view before, DependencyType type = DependencyType::kStrong) &&;
129
130 /// @brief Add dependency for your middleware. Your middleware will be after `MiddlewareAfter` in the pipeline
131 /// @param type is connection type between middlewares
132 template <typename MiddlewareAfter>
133 MiddlewareDependencyBuilder After(DependencyType type = DependencyType::kStrong) &&;
134
135 /// @brief Add dependency for your middleware. Your middleware will be after `after` in the pipeline
136 /// @param type is connection type between middlewares
137 /// @param after is the middleware component name
138 MiddlewareDependencyBuilder After(std::string_view after, DependencyType type = DependencyType::kStrong) &&;
139
140 /// @brief Add dependency for your middleware. Your middleware will be in the `Group` group
141 template <typename Group>
142 MiddlewareDependencyBuilder InGroup() &&;
143
144 /// @cond
145 // Only for internal use.
146 impl::MiddlewareDependency ExtractDependency(std::string_view middleware_name) &&;
147 impl::MiddlewareDependency ExtractGroupDependency(std::string_view group_name) &&;
148 /// @endcond
149
150private:
151 void InUserGroupByDefault();
152
153 impl::MiddlewareDependency dep_{};
154};
155
156namespace impl {
157
158template <typename T>
159concept IsGroup = requires {
160 {
161 T::kDependency
162 } -> std::same_as<const MiddlewareDependencyBuilder&>;
163};
164
165} // namespace impl
166
167template <typename MiddlewareBefore>
168MiddlewareDependencyBuilder MiddlewareDependencyBuilder::Before(DependencyType type) && {
169 if constexpr (impl::IsGroup<MiddlewareBefore>) {
170 return std::move(*this).Before(impl::BeginOfGroup<MiddlewareBefore>(), type);
171 }
172 return std::move(*this).Before(MiddlewareBefore::kName, type);
173}
174
175template <typename MiddlewareAfter>
176MiddlewareDependencyBuilder MiddlewareDependencyBuilder::After(DependencyType type) && {
177 if constexpr (impl::IsGroup<MiddlewareAfter>) {
178 return std::move(*this).After(impl::EndOfGroup<MiddlewareAfter>(), type);
179 }
180 return std::move(*this).After(MiddlewareAfter::kName, type);
181}
182
183template <typename Group>
184MiddlewareDependencyBuilder MiddlewareDependencyBuilder::InGroup() && {
185 dep_.group = Group::kName;
186 dep_.afters.push_back(impl::Connect{impl::BeginOfGroup<Group>(), DependencyType::kStrong});
187 return std::move(*this).Before(impl::EndOfGroup<Group>(), DependencyType::kWeak);
188}
189
190} // namespace middlewares
192USERVER_NAMESPACE_END