userver: /data/code/userver/libraries/grpc-protovalidate/src/grpc-protovalidate/validate.cpp Source File
Loading...
Searching...
No Matches
validate.cpp
1#include <userver/grpc-protovalidate/validate.hpp>
2
3#include <stdexcept>
4#include <string>
5#include <utility>
6#include <vector>
7
8#include <fmt/core.h>
9#include <google/protobuf/arena.h>
10#include <google/protobuf/message.h>
11#include <google/protobuf/repeated_ptr_field.h>
12#include <google/rpc/status.pb.h>
13#include <grpcpp/support/status.h>
14
15#include <grpc-protovalidate/impl/utils.hpp>
16#include <userver/compiler/thread_local.hpp>
17#include <userver/grpc-protovalidate/buf_validate.hpp>
18#include <userver/ugrpc/status_utils.hpp>
19#include <userver/utils/assert.hpp>
20
21USERVER_NAMESPACE_BEGIN
22
23namespace grpc_protovalidate {
24
25namespace {
26
27compiler::ThreadLocal kValidatorFactory = [] { return impl::CreateProtoValidatorFactory(); };
28
29} // namespace
30
31ValidationError::ValidationError(Type type, std::string description, std::string message_name)
32 : type_(type),
33 description_(fmt::format("Message '{}' validation error: {}", message_name, std::move(description))),
34 result_(std::nullopt),
35 message_name_(std::move(message_name))
36{}
37
38ValidationError::ValidationError(buf::validate::ValidationResult result, std::string message_name)
39 : type_(Type::kRule),
40 description_(fmt::format(
41 "Message '{}' validation error: {} constraint(s) violated",
42 message_name,
43 result.violations_size()
44 )),
45 result_(std::move(result)),
46 message_name_(std::move(message_name))
47{}
48
49ValidationError::Type ValidationError::GetType() const { return type_; }
50
51const std::string& ValidationError::GetMessageName() const { return message_name_; }
52
53const std::string& ValidationError::GetDescription() const { return description_; }
54
55const std::vector<buf::validate::RuleViolation>& ValidationError::GetViolations() const {
56 if (GetType() == Type::kInternal || !result_.has_value()) {
57 static constexpr std::vector<buf::validate::RuleViolation> kNoViolations;
58 return kNoViolations;
59 }
60 return result_->violations();
61}
62
63grpc::Status ValidationError::GetGrpcStatus(bool include_violations) const {
64 google::rpc::Status gstatus;
65 gstatus.set_message(GetDescription());
66 switch (GetType()) {
67 case Type::kInternal:
68 gstatus.set_code(grpc::StatusCode::INTERNAL);
69 break;
70 case Type::kRule: {
71 gstatus.set_code(grpc::StatusCode::INVALID_ARGUMENT);
72 break;
73 }
74 }
75 if (include_violations) {
76 gstatus.add_details()->PackFrom(MakeViolationsProto());
77 }
78 return ugrpc::ToGrpcStatus(gstatus);
79}
80
81buf::validate::Violations ValidationError::MakeViolationsProto() const {
82 buf::validate::Violations proto;
83 std::transform(
84 GetViolations().begin(),
85 GetViolations().end(),
86 RepeatedPtrFieldBackInserter(proto.mutable_violations()),
87 [](const buf::validate::RuleViolation& violation) { return violation.proto(); }
88 );
89 return proto;
90}
91
92logging::LogHelper& operator<<(logging::LogHelper& lh, const ValidationError& error) {
93 lh << error.GetDescription();
94 for (const buf::validate::RuleViolation& violation : error.GetViolations()) {
95 lh << violation.proto();
96 }
97 return lh;
98}
99
100ValidationResult::ValidationResult(ValidationError error)
101 : error_(std::move(error))
102{}
103
104bool ValidationResult::IsSuccess() const { return !error_.has_value(); }
105
106const ValidationError& ValidationResult::GetError() const& {
107 if (IsSuccess()) {
108 throw std::logic_error("Requested error for success validation result");
109 }
110 return error_.value();
111}
112
113ValidationError&& ValidationResult::GetError() && {
114 if (IsSuccess()) {
115 throw std::logic_error("Requested error for success validation result");
116 }
117 return std::move(error_).value();
118}
119
120ValidationResult ValidateMessage(const google::protobuf::Message& message, const ValidationParams& params) {
121 auto validator_factory = kValidatorFactory.Use();
122 google::protobuf::Arena arena;
123 auto validator = (*validator_factory)->NewValidator(&arena, params.fail_fast);
124 auto result = validator.Validate(message);
125 if (!result.ok()) {
126 return ValidationError(
127 ValidationError::Type::kInternal,
128 fmt::format(
129 "internal protovalidate error (check constraints syntax in the proto file) - {}",
130 result.status().ToString()
131 ),
132 message.GetTypeName()
133 );
134 }
135 if (result->violations_size() != 0) {
136 return ValidationError(result.value(), message.GetTypeName());
137 }
138 return ValidationResult();
139}
140
141} // namespace grpc_protovalidate
142
143USERVER_NAMESPACE_END