userver: userver/ugrpc/server/middlewares/pipeline.hpp Source File
Loading...
Searching...
No Matches
pipeline.hpp
1#pragma once
2
3/// @file userver/ugrpc/server/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/ugrpc/server/impl/middleware_pipeline_config.hpp>
14#include <userver/ugrpc/server/middlewares/fwd.hpp>
15#include <userver/ugrpc/server/service_base.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace ugrpc::server {
20
21/// @brief The dependency type between middlewares.
22///
23/// Iff dependency type from 'X' to 'Y' is `kStrong` and 'Y' is disabled, userver will failure when start,
24/// otherwise (in `kWeak`) we ignore this dependency.
25enum class DependencyType {
26 kStrong = 0,
27 kWeak = 1,
28};
29
30namespace impl {
31
32struct Connect final {
33 std::string node_name{};
34 DependencyType type{DependencyType::kStrong};
35};
36
37struct MiddlewareDependency final {
38 std::string middleware_name{};
39 std::vector<Connect> befores{};
40 std::vector<Connect> afters{};
41 bool enabled{true};
42 std::optional<std::string> group{};
43};
44
45using Dependencies = std::unordered_map<std::string, MiddlewareDependency>;
46
47class MiddlewarePipeline final {
48public:
49 explicit MiddlewarePipeline(Dependencies&& deps);
50
51 std::vector<std::string> GetPerServiceMiddlewares(const MiddlewareServiceConfig& config) const;
52
53 const MiddlewareOrderedList& GetOrderedList() const { return pipeline_; }
54
55private:
56 Dependencies deps_{};
57 MiddlewareOrderedList pipeline_{};
58};
59
60template <typename Group>
61std::string BeginOfGroup() {
62 return std::string{Group::kName} + "#begin";
63}
64
65template <typename Group>
66std::string EndOfGroup() {
67 return std::string{Group::kName} + "#end";
68}
69
70} // namespace impl
71
72/// @brief Component to create middlewares pipeline.
73///
74/// Your must register your grpc-server middleware in this component.
75/// Use `MiddlewareDependencyBuilder` to set a dependency of your middleware from others.
76///
77/// ## Static options:
78/// Name | Description | Default value
79/// ---- | ----------- | -------------
80/// middlewares | middlewares names to use | `{}`
81class MiddlewarePipelineComponent final : public components::ComponentBase {
82public:
83 /// @ingroup userver_component_names
84 /// @brief The default name of ugrpc::server::MiddlewarePipelineComponent
85 static constexpr std::string_view kName = "grpc-server-middlewares-pipeline";
86
87 MiddlewarePipelineComponent(const components::ComponentConfig& config, const components::ComponentContext& context);
88
89 static yaml_config::Schema GetStaticConfigSchema();
90
91 /// @cond
92 /// Only for internal use.
93 const impl::MiddlewarePipeline& GetPipeline() const;
94 /// @endcond
95
96private:
97 impl::MiddlewarePipeline pipeline_;
98};
99
100/// @brief class for building a middleware dependency.
101///
102/// If you don't care about the order in relative to others, ignore this and your middleware
103/// will be in the `kUser` group.
104/// Otherwise, pass a instance of this class to `MiddlewareComponentBase` in the constructor of your
105/// middleware component.
106class MiddlewareDependencyBuilder final {
107public:
108 /// @brief Builder for middleware dependencey
109 /// @param priority is middleware priority
110 explicit MiddlewareDependencyBuilder() = default;
111
112 MiddlewareDependencyBuilder(const MiddlewareDependencyBuilder&) = default;
113
114 MiddlewareDependencyBuilder(MiddlewareDependencyBuilder&&) noexcept = default;
115
116 /// @brief Add dependency for your middleware. Your middleware will be before 'MiddlewareBefore' in the pipeline
117 /// @param type is connect type between middlewares
118 template <typename MiddlewareBefore>
119 MiddlewareDependencyBuilder Before(DependencyType type = DependencyType::kStrong) &&;
120
121 /// @brief Add dependency for your middleware. Your middleware will be before 'before' in the pipeline
122 /// @param type is connect type between middlewares
123 /// @param before is the middleware component name
124 MiddlewareDependencyBuilder Before(std::string_view before, DependencyType type = DependencyType::kStrong) &&;
125
126 /// @brief Add dependency for your middleware. Your middleware will be after 'MiddlewareAfter' in the pipeline
127 /// @param type is connect type between middlewares
128 template <typename MiddlewareAfter>
129 MiddlewareDependencyBuilder After(DependencyType type = DependencyType::kStrong) &&;
130
131 /// @brief Add dependency for your middleware. Your middleware will be after 'after' in the pipeline
132 /// @param type is connect type between middlewares
133 /// @param after is the middleware component name
134 MiddlewareDependencyBuilder After(std::string_view after, DependencyType type = DependencyType::kStrong) &&;
135
136 /// @brief Add dependency for your middleware. Your middleware will be in the 'Group' group
137 /// @param type is type of Group
138 template <typename Group>
139 MiddlewareDependencyBuilder InGroup() &&;
140
141 /// @cond
142 /// Only for internal use.
143 impl::MiddlewareDependency Extract(std::string_view middleware_name) &&;
144 /// @endcond
145
146private:
147 impl::MiddlewareDependency dep_{};
148};
149
150namespace impl {
151
152template <typename T>
153using IsGroup = decltype(T::dependency);
154
155template <typename T>
156constexpr bool kIsGroup = std::is_same_v<meta::DetectedType<IsGroup, T>, const MiddlewareDependencyBuilder>;
157
158} // namespace impl
159
160template <typename MiddlewareBefore>
161MiddlewareDependencyBuilder MiddlewareDependencyBuilder::Before(DependencyType type) && {
162 if constexpr (impl::kIsGroup<MiddlewareBefore>) {
163 return std::move(*this).Before(impl::BeginOfGroup<MiddlewareBefore>(), type);
164 }
165 return std::move(*this).Before(MiddlewareBefore::kName, type);
166}
167
168template <typename MiddlewareAfter>
169MiddlewareDependencyBuilder MiddlewareDependencyBuilder::After(DependencyType type) && {
170 if constexpr (impl::kIsGroup<MiddlewareAfter>) {
171 return std::move(*this).After(impl::EndOfGroup<MiddlewareAfter>(), type);
172 }
173 return std::move(*this).After(MiddlewareAfter::kName, type);
174}
175
176template <typename Group>
177MiddlewareDependencyBuilder MiddlewareDependencyBuilder::InGroup() && {
178 dep_.group = Group::kName;
179 dep_.afters.push_back(impl::Connect{impl::BeginOfGroup<Group>(), DependencyType::kStrong});
180 return std::move(*this).Before(impl::EndOfGroup<Group>(), DependencyType::kWeak);
181}
182
183} // namespace ugrpc::server
184
185template <>
186inline constexpr bool components::kHasValidate<ugrpc::server::MiddlewarePipelineComponent> = true;
187
188template <>
189inline constexpr auto components::kConfigFileMode<ugrpc::server::MiddlewarePipelineComponent> =
191
192USERVER_NAMESPACE_END