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>
159using IsGroup = decltype(T::kDependency);
160
161template <typename T>
162constexpr bool kIsGroup = std::is_same_v<meta::DetectedType<IsGroup, T>, const MiddlewareDependencyBuilder>;
163
164} // namespace impl
165
166template <typename MiddlewareBefore>
167MiddlewareDependencyBuilder MiddlewareDependencyBuilder::Before(DependencyType type) && {
168 if constexpr (impl::kIsGroup<MiddlewareBefore>) {
169 return std::move(*this).Before(impl::BeginOfGroup<MiddlewareBefore>(), type);
170 }
171 return std::move(*this).Before(MiddlewareBefore::kName, type);
172}
173
174template <typename MiddlewareAfter>
175MiddlewareDependencyBuilder MiddlewareDependencyBuilder::After(DependencyType type) && {
176 if constexpr (impl::kIsGroup<MiddlewareAfter>) {
177 return std::move(*this).After(impl::EndOfGroup<MiddlewareAfter>(), type);
178 }
179 return std::move(*this).After(MiddlewareAfter::kName, type);
180}
181
182template <typename Group>
183MiddlewareDependencyBuilder MiddlewareDependencyBuilder::InGroup() && {
184 dep_.group = Group::kName;
185 dep_.afters.push_back(impl::Connect{impl::BeginOfGroup<Group>(), DependencyType::kStrong});
186 return std::move(*this).Before(impl::EndOfGroup<Group>(), DependencyType::kWeak);
187}
188
189} // namespace middlewares
191USERVER_NAMESPACE_END