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
37ValidationError::ValidationError(buf::validate::ValidationResult result, std::string message_name)
38 : type_(Type::kRule),
39 description_(fmt::format(
40 "Message '{}' validation error: {} constraint(s) violated",
41 message_name,
42 result.violations_size()
43 )),
44 result_(std::move(result)),
45 message_name_(std::move(message_name)) {}
46
47ValidationError::Type ValidationError::GetType() const { return type_; }
48
49const std::string& ValidationError::GetMessageName() const { return message_name_; }
50
51const std::string& ValidationError::GetDescription() const { return description_; }
52
53const std::vector<buf::validate::RuleViolation>& ValidationError::GetViolations() const {
54 if (GetType() == Type::kInternal || !result_.has_value()) {
55 static constexpr std::vector<buf::validate::RuleViolation> kNoViolations;
56 return kNoViolations;
57 }
58 return result_->violations();
59}
60
61grpc::Status ValidationError::GetGrpcStatus(bool include_violations) const {
62 google::rpc::Status gstatus;
63 gstatus.set_message(GetDescription());
64 switch (GetType()) {
65 case Type::kInternal:
66 gstatus.set_code(grpc::StatusCode::INTERNAL);
67 break;
68 case Type::kRule: {
69 gstatus.set_code(grpc::StatusCode::INVALID_ARGUMENT);
70 break;
71 }
72 }
73 if (include_violations) {
74 gstatus.add_details()->PackFrom(MakeViolationsProto());
75 }
76 return ugrpc::ToGrpcStatus(gstatus);
77}
78
79buf::validate::Violations ValidationError::MakeViolationsProto() const {
80 buf::validate::Violations proto;
81 std::transform(
82 GetViolations().begin(),
83 GetViolations().end(),
84 RepeatedPtrFieldBackInserter(proto.mutable_violations()),
85 [](const buf::validate::RuleViolation& violation) { return violation.proto(); }
86 );
87 return proto;
88}
89
90logging::LogHelper& operator<<(logging::LogHelper& lh, const ValidationError& error) {
91 lh << error.GetDescription();
92 for (const buf::validate::RuleViolation& violation : error.GetViolations()) {
93 lh << violation.proto();
94 }
95 return lh;
96}
97
98ValidationResult::ValidationResult(ValidationError error) : error_(std::move(error)) {}
99
100bool ValidationResult::IsSuccess() const { return !error_.has_value(); }
101
102const ValidationError& ValidationResult::GetError() const& {
103 if (IsSuccess()) {
104 throw std::logic_error("Requested error for success validation result");
105 }
106 return error_.value();
107}
108
109ValidationError&& ValidationResult::GetError() && {
110 if (IsSuccess()) {
111 throw std::logic_error("Requested error for success validation result");
112 }
113 return std::move(error_).value();
114}
115
116ValidationResult ValidateMessage(const google::protobuf::Message& message, const ValidationParams& params) {
117 auto validator_factory = kValidatorFactory.Use();
118 google::protobuf::Arena arena;
119 auto validator = (*validator_factory)->NewValidator(&arena, params.fail_fast);
120 auto result = validator.Validate(message);
121 if (!result.ok()) {
122 return ValidationError(
123 ValidationError::Type::kInternal,
124 fmt::format(
125 "internal protovalidate error (check constraints syntax in the proto file) - {}",
126 result.status().ToString()
127 ),
128 message.GetTypeName()
129 );
130 }
131 if (result->violations_size() != 0) {
132 return ValidationError(result.value(), message.GetTypeName());
133 }
134 return ValidationResult();
135}
136
137} // namespace grpc_protovalidate
138
139USERVER_NAMESPACE_END