From 2acc0fc3cdc40434ea286f2fac62386e3fd9c19d Mon Sep 17 00:00:00 2001 From: Daniil Cherednik Date: Fri, 18 Mar 2022 05:49:29 +0300 Subject: Move validation proto plugin in to public/lib KIKIMR-14468 ref:c691672a442302ec0c05bb0528429891546971f6 --- CMakeLists.darwin.txt | 6 +- CMakeLists.linux.txt | 6 +- .../grpc_services/validation/CMakeLists.darwin.txt | 29 - .../grpc_services/validation/CMakeLists.linux.txt | 34 -- ydb/core/grpc_services/validation/CMakeLists.txt | 13 - ydb/core/grpc_services/validation/helpers.cpp | 66 --- ydb/core/grpc_services/validation/helpers.h | 19 - ydb/core/grpc_services/validation/main.cpp | 581 --------------------- ydb/core/grpc_services/validation/ut.cpp | 288 ---------- .../validation/ut/CMakeLists.darwin.txt | 43 -- .../validation/ut/CMakeLists.linux.txt | 48 -- .../grpc_services/validation/ut/CMakeLists.txt | 13 - .../validation/ut/protos/CMakeLists.txt | 23 - .../validation/ut/protos/validation_test.proto | 60 --- ydb/public/lib/validation/CMakeLists.darwin.txt | 29 + ydb/public/lib/validation/CMakeLists.linux.txt | 34 ++ ydb/public/lib/validation/CMakeLists.txt | 13 + ydb/public/lib/validation/helpers.cpp | 66 +++ ydb/public/lib/validation/helpers.h | 19 + ydb/public/lib/validation/main.cpp | 581 +++++++++++++++++++++ ydb/public/lib/validation/ut.cpp | 288 ++++++++++ ydb/public/lib/validation/ut/CMakeLists.darwin.txt | 43 ++ ydb/public/lib/validation/ut/CMakeLists.linux.txt | 48 ++ ydb/public/lib/validation/ut/CMakeLists.txt | 13 + ydb/public/lib/validation/ut/protos/CMakeLists.txt | 23 + .../lib/validation/ut/protos/validation_test.proto | 60 +++ 26 files changed, 1223 insertions(+), 1223 deletions(-) delete mode 100644 ydb/core/grpc_services/validation/CMakeLists.darwin.txt delete mode 100644 ydb/core/grpc_services/validation/CMakeLists.linux.txt delete mode 100644 ydb/core/grpc_services/validation/CMakeLists.txt delete mode 100644 ydb/core/grpc_services/validation/helpers.cpp delete mode 100644 ydb/core/grpc_services/validation/helpers.h delete mode 100644 ydb/core/grpc_services/validation/main.cpp delete mode 100644 ydb/core/grpc_services/validation/ut.cpp delete mode 100644 ydb/core/grpc_services/validation/ut/CMakeLists.darwin.txt delete mode 100644 ydb/core/grpc_services/validation/ut/CMakeLists.linux.txt delete mode 100644 ydb/core/grpc_services/validation/ut/CMakeLists.txt delete mode 100644 ydb/core/grpc_services/validation/ut/protos/CMakeLists.txt delete mode 100644 ydb/core/grpc_services/validation/ut/protos/validation_test.proto create mode 100644 ydb/public/lib/validation/CMakeLists.darwin.txt create mode 100644 ydb/public/lib/validation/CMakeLists.linux.txt create mode 100644 ydb/public/lib/validation/CMakeLists.txt create mode 100644 ydb/public/lib/validation/helpers.cpp create mode 100644 ydb/public/lib/validation/helpers.h create mode 100644 ydb/public/lib/validation/main.cpp create mode 100644 ydb/public/lib/validation/ut.cpp create mode 100644 ydb/public/lib/validation/ut/CMakeLists.darwin.txt create mode 100644 ydb/public/lib/validation/ut/CMakeLists.linux.txt create mode 100644 ydb/public/lib/validation/ut/CMakeLists.txt create mode 100644 ydb/public/lib/validation/ut/protos/CMakeLists.txt create mode 100644 ydb/public/lib/validation/ut/protos/validation_test.proto diff --git a/CMakeLists.darwin.txt b/CMakeLists.darwin.txt index 78fa522f972..22e1b582be5 100644 --- a/CMakeLists.darwin.txt +++ b/CMakeLists.darwin.txt @@ -112,7 +112,7 @@ add_subdirectory(ydb/library/yql/public/issue) add_subdirectory(ydb/public/api/protos) add_subdirectory(ydb/public/api/protos/annotations) add_subdirectory(tools/enum_parser/enum_serialization_runtime) -add_subdirectory(ydb/core/grpc_services/validation) +add_subdirectory(ydb/public/lib/validation) add_subdirectory(tools/enum_parser/enum_parser/bin) add_subdirectory(tools/enum_parser/parse_enum) add_subdirectory(library/cpp/cppparser) @@ -938,8 +938,6 @@ add_subdirectory(ydb/core/erasure/ut_rope) add_subdirectory(ydb/core/erasure/ut_perf) add_subdirectory(ydb/core/formats/ut) add_subdirectory(ydb/core/grpc_services/ut) -add_subdirectory(ydb/core/grpc_services/validation/ut) -add_subdirectory(ydb/core/grpc_services/validation/ut/protos) add_subdirectory(ydb/core/grpc_streaming/ut) add_subdirectory(ydb/core/grpc_streaming/ut/grpc) add_subdirectory(ydb/core/health_check/ut) @@ -1171,6 +1169,8 @@ add_subdirectory(ydb/library/yql/utils/log/ut) add_subdirectory(ydb/public/lib/idx_test/ut) add_subdirectory(ydb/public/lib/json_value/ut) add_subdirectory(ydb/public/lib/operation_id/ut) +add_subdirectory(ydb/public/lib/validation/ut) +add_subdirectory(ydb/public/lib/validation/ut/protos) add_subdirectory(ydb/public/sdk/cpp/client/ydb_persqueue_core/ut/with_offset_ranges_mode_ut) add_subdirectory(ydb/public/sdk/cpp/client/ydb_driver/ut) add_subdirectory(ydb/public/sdk/cpp/client/ydb_table/impl/ut) diff --git a/CMakeLists.linux.txt b/CMakeLists.linux.txt index 835cb8ed361..a3112c0e0d5 100644 --- a/CMakeLists.linux.txt +++ b/CMakeLists.linux.txt @@ -190,7 +190,7 @@ add_subdirectory(ydb/library/yql/public/issue) add_subdirectory(ydb/public/api/protos) add_subdirectory(ydb/public/api/protos/annotations) add_subdirectory(tools/enum_parser/enum_serialization_runtime) -add_subdirectory(ydb/core/grpc_services/validation) +add_subdirectory(ydb/public/lib/validation) add_subdirectory(tools/enum_parser/enum_parser/bin) add_subdirectory(tools/enum_parser/parse_enum) add_subdirectory(library/cpp/cppparser) @@ -1032,8 +1032,6 @@ add_subdirectory(ydb/core/erasure/ut_rope) add_subdirectory(ydb/core/erasure/ut_perf) add_subdirectory(ydb/core/formats/ut) add_subdirectory(ydb/core/grpc_services/ut) -add_subdirectory(ydb/core/grpc_services/validation/ut) -add_subdirectory(ydb/core/grpc_services/validation/ut/protos) add_subdirectory(ydb/core/grpc_streaming/ut) add_subdirectory(ydb/core/grpc_streaming/ut/grpc) add_subdirectory(ydb/core/health_check/ut) @@ -1266,6 +1264,8 @@ add_subdirectory(ydb/library/yql/utils/log/ut) add_subdirectory(ydb/public/lib/idx_test/ut) add_subdirectory(ydb/public/lib/json_value/ut) add_subdirectory(ydb/public/lib/operation_id/ut) +add_subdirectory(ydb/public/lib/validation/ut) +add_subdirectory(ydb/public/lib/validation/ut/protos) add_subdirectory(ydb/public/sdk/cpp/client/ydb_persqueue_core/ut/with_offset_ranges_mode_ut) add_subdirectory(ydb/public/sdk/cpp/client/ydb_driver/ut) add_subdirectory(ydb/public/sdk/cpp/client/ydb_table/impl/ut) diff --git a/ydb/core/grpc_services/validation/CMakeLists.darwin.txt b/ydb/core/grpc_services/validation/CMakeLists.darwin.txt deleted file mode 100644 index 011bb7896de..00000000000 --- a/ydb/core/grpc_services/validation/CMakeLists.darwin.txt +++ /dev/null @@ -1,29 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - - -add_executable(validation) -target_link_libraries(validation PUBLIC - contrib-libs-cxxsupp - yutil - library-cpp-cpuid_check - contrib-libs-protoc - api-protos-annotations -) -target_sources(validation PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/helpers.cpp - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/main.cpp -) -target_link_flags(validation - PUBLIC - -Wl,-no_deduplicate - -Wl,-sdk_version,10.15 - -fPIC - -fPIC -) -vcs_info(validation) diff --git a/ydb/core/grpc_services/validation/CMakeLists.linux.txt b/ydb/core/grpc_services/validation/CMakeLists.linux.txt deleted file mode 100644 index 74ba528506d..00000000000 --- a/ydb/core/grpc_services/validation/CMakeLists.linux.txt +++ /dev/null @@ -1,34 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - - -add_executable(validation) -target_link_libraries(validation PUBLIC - contrib-libs-cxxsupp - yutil - library-cpp-lfalloc - library-cpp-cpuid_check - contrib-libs-protoc - api-protos-annotations -) -target_sources(validation PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/helpers.cpp - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/main.cpp -) -target_link_flags(validation - PUBLIC - -ldl - -lrt - -Wl,--no-as-needed - -fPIC - -fPIC - -lpthread - -lrt - -ldl -) -vcs_info(validation) diff --git a/ydb/core/grpc_services/validation/CMakeLists.txt b/ydb/core/grpc_services/validation/CMakeLists.txt deleted file mode 100644 index a681d385f3e..00000000000 --- a/ydb/core/grpc_services/validation/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - -if (APPLE) - include(CMakeLists.darwin.txt) -elseif (UNIX) - include(CMakeLists.linux.txt) -endif() diff --git a/ydb/core/grpc_services/validation/helpers.cpp b/ydb/core/grpc_services/validation/helpers.cpp deleted file mode 100644 index 0b30fdcec79..00000000000 --- a/ydb/core/grpc_services/validation/helpers.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "helpers.h" - -#include -#include -#include -#include -#include - -#include -#include - -namespace NKikimr { -namespace NValidation { - -static TString ProtoFileNameStripped(const google::protobuf::Descriptor* message) { - return google::protobuf::compiler::cpp::StripProto(message->file()->name()); -} - -TString HeaderFileName(const google::protobuf::Descriptor* message) { - return ProtoFileNameStripped(message).append(".pb.h"); -} - -TString SourceFileName(const google::protobuf::Descriptor* message) { - return ProtoFileNameStripped(message).append(".pb.cc"); -} - -TString ClassScope(const google::protobuf::Descriptor* message) { - return "class_scope:" + message->full_name(); -} - -TString NamespaceScope() { - return "namespace_scope"; -} - -TString ClassName(const google::protobuf::Descriptor* message) { - const TString ns = message->file()->package(); - TString className = !ns.empty() ? message->full_name().substr(ns.size() + 1) : message->full_name(); - SubstGlobal(className, ".", "::"); - return className; -} - -bool IsCustomMessage(const google::protobuf::Descriptor* message) { - if (!message) { - return false; - } - if (message->full_name() == google::protobuf::Any::descriptor()->full_name()) { - return false; - } - if (message->full_name() == google::protobuf::Duration::descriptor()->full_name()) { - return false; - } - if (message->full_name() == google::protobuf::Empty::descriptor()->full_name()) { - return false; - } - if (message->full_name() == google::protobuf::Timestamp::descriptor()->full_name()) { - return false; - } - if (message->options().map_entry()) { - return false; - } - - return true; -} - -} // NValidation -} // NKikimr diff --git a/ydb/core/grpc_services/validation/helpers.h b/ydb/core/grpc_services/validation/helpers.h deleted file mode 100644 index 60047a9dd78..00000000000 --- a/ydb/core/grpc_services/validation/helpers.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -#include - -namespace NKikimr { -namespace NValidation { - -TString HeaderFileName(const google::protobuf::Descriptor* message); -TString SourceFileName(const google::protobuf::Descriptor* message); -TString ClassScope(const google::protobuf::Descriptor* message); -TString NamespaceScope(); -TString ClassName(const google::protobuf::Descriptor* message); - -bool IsCustomMessage(const google::protobuf::Descriptor* message); - -} // NValidation -} // NKikimr diff --git a/ydb/core/grpc_services/validation/main.cpp b/ydb/core/grpc_services/validation/main.cpp deleted file mode 100644 index 68c4bda65cb..00000000000 --- a/ydb/core/grpc_services/validation/main.cpp +++ /dev/null @@ -1,581 +0,0 @@ -#include "helpers.h" - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace NKikimr { -namespace NValidation { - -using namespace google::protobuf::compiler; -using namespace google::protobuf; - -using TVariables = std::map; - -class TPrinter { -public: - explicit TPrinter(OutputDirectory* output, const TString& fileName, const TString& scope) - : Output(output) - , FileName(fileName) - , Scope(scope) - { - } - - io::Printer* operator->() { - if (!Printer) { - Stream.Reset(Output->OpenForInsert(FileName, Scope)); - Printer.ConstructInPlace(Stream.Get(), '$'); - } - - return Printer.Get(); - } - -private: - OutputDirectory* Output; - const TString FileName; - const TString Scope; - - THolder Stream; - TMaybe Printer; - -}; // TPrinter - -class TFieldGenerator: public TThrRefBase { - void Required(TPrinter& printer) const { - Y_VERIFY(!Field->is_repeated(), "Repeated fields cannot be required or not"); - - if (Field->options().GetExtension(Ydb::required)) { - if (Field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { - printer->Print(Vars, "if ($field$().empty()) {\n"); - } else if (Field->message_type()) { - printer->Print(Vars, "if (!has_$field$()) {\n"); - } else { - Y_FAIL_S(" Field type = " << Field->type_name() << " cannot be required or not"); - } - - printer->Indent(); - printer->Print(Vars, "__err = \"$field$ is required but not set\";\n"); - printer->Print(Vars, "return false;\n"); - printer->Outdent(); - printer->Print(Vars, "}\n"); - } - } - - void CheckLimit(TPrinter& printer, const Ydb::Limit& limit, TVariables vars) const { - switch (limit.kind_case()) { - case Ydb::Limit::kRange: - vars["min"] = ToString(limit.range().min()); - vars["max"] = ToString(limit.range().max()); - printer->Print(vars, "if (!($min$ <= $getter$ && $getter$ <= $max$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not in [$min$; $max$]\";\n"); - break; - - case Ydb::Limit::kLt: - vars["lt"] = ToString(limit.lt()); - printer->Print(vars, "if (!($getter$ < $lt$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not < $lt$\";\n"); - break; - - case Ydb::Limit::kLe: - vars["le"] = ToString(limit.le()); - printer->Print(vars, "if (!($getter$ <= $le$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not <= $le$\";\n"); - break; - - case Ydb::Limit::kEq: - vars["eq"] = ToString(limit.eq()); - printer->Print(vars, "if (!($getter$ == $eq$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not == $eq$\";\n"); - break; - - case Ydb::Limit::kGe: - vars["ge"] = ToString(limit.ge()); - printer->Print(vars, "if (!($getter$ >= $ge$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not >= $ge$\";\n"); - break; - - case Ydb::Limit::kGt: - vars["gt"] = ToString(limit.gt()); - printer->Print(vars, "if (!($getter$ > $gt$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not > $gt$\";\n"); - break; - - default: - Y_FAIL("Unknown limit type"); - } - - printer->Print(vars, "return false;\n"); - printer->Outdent(); - printer->Print(vars, "}\n"); - } - - static TString BuildValueChecker(const TString& getter, TStringBuf annValue) { - if (annValue.Contains('<') || annValue.Contains('>') || annValue.Contains('=')) { - return TStringBuilder() << getter << " " << annValue; - } - - if ((annValue.StartsWith('[') || annValue.StartsWith('(')) - && (annValue.EndsWith(']') || annValue.EndsWith(')')) - && annValue.Contains(';')) { - - const bool leftInclusive = annValue.StartsWith('['); - const bool rightInclusive = annValue.EndsWith(']'); - - annValue.Skip(1).Chop(1); - - const TStringBuf left = annValue.Before(';'); - const TStringBuf right = annValue.After(';'); - - return TStringBuilder() - << left << " " << (leftInclusive ? "<=" : "<") - << " " << getter << " && " << getter << " " - << (rightInclusive ? "<=" : "<") << " " << right; - } - - Y_FAIL_S("Invalid value: " << annValue); - } - - void CheckValue(TPrinter& printer, const FieldDescriptor* field, TVariables vars) const { - switch (field->cpp_type()) { - case FieldDescriptor::CPPTYPE_INT32: - case FieldDescriptor::CPPTYPE_INT64: - case FieldDescriptor::CPPTYPE_UINT32: - case FieldDescriptor::CPPTYPE_UINT64: - case FieldDescriptor::CPPTYPE_DOUBLE: - case FieldDescriptor::CPPTYPE_FLOAT: - case FieldDescriptor::CPPTYPE_BOOL: - case FieldDescriptor::CPPTYPE_STRING: - printer->Print(vars, "if (!($value_checker$)) {\n"); - printer->Indent(); - printer->Print(vars, "__err = \"$field$$kind$ is not $value$\";\n"); - printer->Print(vars, "return false;\n"); - printer->Outdent(); - printer->Print(vars, "}\n"); - break; - - default: - Y_FAIL_S("Cannot check value of field: type = " << field->type_name()); - } - } - - void Size(TPrinter& printer) const { - Y_VERIFY(Field->is_repeated(), "Cannot check size of non-repeated field"); - - TVariables vars = Vars; - vars["kind"] = " size"; - vars["getter"] = Field->name() + "_size()"; - - CheckLimit(printer, Field->options().GetExtension(Ydb::size), vars); - } - - void Length(TPrinter& printer) const { - if (Field->is_repeated()) { - printer->Print(Vars, "for (const auto& value : $field$()) {\n"); - printer->Indent(); - } - - if (Field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { - TVariables vars = Vars; - vars["kind"] = "'s length"; - vars["getter"] = Field->is_repeated() ? "value.size()" : Field->name() + "().size()"; - - CheckLimit(printer, Field->options().GetExtension(Ydb::length), vars); - } else if (Field->is_map()) { - const FieldDescriptor* value = Field->message_type()->FindFieldByName("value"); - Y_VERIFY_S(value->cpp_type() == FieldDescriptor::CPPTYPE_STRING, "Cannot check length of field: type = " << value->type_name()); - - TVariables vars = Vars; - vars["kind"] = "'s value length"; - vars["getter"] = "value.second.size()"; - - CheckLimit(printer, Field->options().GetExtension(Ydb::length), vars); - } else { - Y_FAIL_S("Cannot check length of field: type = " << Field->type_name()); - } - - if (Field->is_repeated()) { - printer->Outdent(); - printer->Print(Vars, "}\n"); - } - } - - void MapKey(TPrinter& printer) const { - Y_VERIFY(Field->is_map(), "Cannot validate map key of non-map field"); - - printer->Print(Vars, "for (const auto& value : $field$()) {\n"); - printer->Indent(); - - TVariables vars = Vars; - - const auto& mapKey = Field->options().GetExtension(Ydb::map_key); - if (mapKey.has_length()) { - const FieldDescriptor* key = Field->message_type()->FindFieldByName("key"); - Y_VERIFY_S(key->cpp_type() == FieldDescriptor::CPPTYPE_STRING, "Cannot check length of field: type = " << key->type_name()); - - vars["kind"] = "'s key length"; - vars["getter"] = "value.first.size()"; - CheckLimit(printer, mapKey.length(), vars); - } - if (mapKey.value()) { - vars["kind"] = "'s key"; - vars["value"] = SubstGlobalCopy(mapKey.value(), '"', '\''); - vars["value_checker"] = BuildValueChecker("value.first", mapKey.value()); - CheckValue(printer, Field->message_type()->FindFieldByName("key"), vars); - } - - printer->Outdent(); - printer->Print(Vars, "}\n"); - } - - void Value(TPrinter& printer) const { - if (Field->is_repeated()) { - printer->Print(Vars, "for (const auto& value : $field$()) {\n"); - printer->Indent(); - } - - const TString value = Field->options().GetExtension(Ydb::value); - - TVariables vars = Vars; - vars["kind"] = "'s value"; - vars["value"] = SubstGlobalCopy(value, '"', '\''); - - if (Field->is_map()) { - vars["value_checker"] = BuildValueChecker("value.second", value); - CheckValue(printer, Field->message_type()->FindFieldByName("value"), vars); - } else { - vars["value_checker"] = BuildValueChecker(Field->is_repeated() ? "value" : Field->name() + "()", value); - CheckValue(printer, Field, vars); - } - - if (Field->is_repeated()) { - printer->Outdent(); - printer->Print(Vars, "}\n"); - } - } - - void Body(TPrinter& printer) const { - const auto& opts = Field->options(); - - if (opts.HasExtension(Ydb::required)) { - Required(printer); - } - if (opts.HasExtension(Ydb::size)) { - Size(printer); - } - if (opts.HasExtension(Ydb::length)) { - Length(printer); - } - if (opts.HasExtension(Ydb::map_key)) { - MapKey(printer); - } - if (opts.HasExtension(Ydb::value)) { - Value(printer); - } - - if (IsCustomMessage(Field->message_type())) { - if (Field->is_repeated()) { - printer->Print(Vars, "for (const auto& value : $field$()) {\n"); - printer->Indent(); - printer->Print(Vars, "if (!value.validate(__err)) {\n"); - printer->Indent(); - printer->Print(Vars, "return false;\n"); - printer->Outdent(); - printer->Print(Vars, "}\n"); - printer->Outdent(); - printer->Print(Vars, "}\n"); - printer->Print(Vars, "\nreturn true;\n"); - } else { - printer->Print(Vars, "return $field$().validate(__err);\n"); - } - } else { - printer->Print(Vars, "return true;\n"); - } - } - - bool HasValidators() const { - const auto& opts = Field->options(); - return opts.HasExtension(Ydb::required) - || opts.HasExtension(Ydb::size) - || opts.HasExtension(Ydb::length) - || opts.HasExtension(Ydb::map_key) - || opts.HasExtension(Ydb::value); - } - - static TString PascalName(TString camelName) { - camelName.to_upper(0, 1); - return camelName; - } - -public: - using TPtr = TIntrusivePtr; - - explicit TFieldGenerator(const FieldDescriptor* field, const TString& className) - : Field(field) - , Vars({ - {"class", className}, - {"func", "validate_" + field->name()}, - {"field", field->name()}, - {"PascalName", PascalName(field->camelcase_name())}, - }) - { - } - - const TVariables& GetVars() const { - return Vars; - } - - void Declare(TPrinter& printer) const { - if (!HasValidators() && !IsCustomMessage(Field->message_type())) { - printer->Print(Vars, "bool $func$(TProtoStringType&) const { return true; }\n"); - } else { - printer->Print(Vars, "bool $func$(TProtoStringType& __err) const;\n"); - } - } - - void Define(TPrinter& printer) const { - if (!HasValidators() && !IsCustomMessage(Field->message_type())) { - return; - } - - printer->Print(Vars, "bool $class$::$func$(TProtoStringType& __err) const {\n"); - printer->Indent(); - Body(printer); - printer->Outdent(); - printer->Print(Vars, "}\n\n"); - } - -private: - const FieldDescriptor* Field; - const TVariables Vars; - -}; // TFieldGenerator - -class TOneofGenerator: public TThrRefBase { - void Body(TPrinter& printer) const { - printer->Print(Vars, "switch ($oneof$_case()) {\n"); - - for (auto field : Fields) { - printer->Print(field->GetVars(), "case k$PascalName$:\n"); - printer->Indent(); - printer->Print(field->GetVars(), "return $func$(__err);\n"); - printer->Outdent(); - } - - printer->Print(Vars, "default:\n"); - printer->Indent(); - printer->Print(Vars, "return true;\n"); - printer->Outdent(); - - printer->Print(Vars, "}\n"); - } - -public: - using TPtr = TIntrusivePtr; - - explicit TOneofGenerator(const OneofDescriptor* oneof, const TString& className) - : Vars({ - {"class", className}, - {"oneof", oneof->name()}, - {"func", "validate_" + oneof->name()}, - }) - { - } - - const TVariables& GetVars() const { - return Vars; - } - - void AddField(TFieldGenerator::TPtr field) { - Fields.push_back(field); - } - - void Declare(TPrinter& printer) const { - printer->Print(Vars, "bool $func$(TProtoStringType& __err) const;\n"); - } - - void Define(TPrinter& printer) const { - printer->Print(Vars, "bool $class$::$func$(TProtoStringType& __err) const {\n"); - printer->Indent(); - Body(printer); - printer->Outdent(); - printer->Print(Vars, "}\n\n"); - } - -private: - const TVariables Vars; - TVector Fields; - -}; // TOneofGenerator - -class TMessageGenerator { - struct TItem { - TOneofGenerator::TPtr Oneof; - TVector Fields; - - explicit TItem(TOneofGenerator::TPtr oneof) - : Oneof(oneof) - { - } - - void AddField(TFieldGenerator::TPtr field) { - Fields.push_back(field); - if (Oneof) { - Oneof->AddField(field); - } - } - }; - - using TItems = THashMap; - - void Declare(TPrinter& printer, const TItems& items) { - if (!items) { - printer->Print(Vars, "bool validate(TProtoStringType&) const { return true; }\n"); - } else { - printer->Print(Vars, "bool validate(TProtoStringType& __err) const;\n"); - } - } - - void Define(TPrinter& printer, const TItems& items) { - if (!items) { - return; - } - - printer->Print(Vars, "bool $class$::validate(TProtoStringType& __err) const {\n"); - printer->Indent(); - printer->Print(Vars, "return\n"); - printer->Indent(); - - bool first = true; - for (const auto& [_, item] : items) { - if (!item.Oneof) { - for (const auto& field : item.Fields) { - if (first) { - first = false; - } else { - printer->Print(Vars, "&& "); - } - printer->Print(field->GetVars(), "$func$(__err)\n"); - } - } else { - if (first) { - first = false; - } else { - printer->Print(Vars, "&& "); - } - printer->Print(item.Oneof->GetVars(), "$func$(__err)\n"); - } - } - - printer->Outdent(); - printer->Print(Vars, ";\n"); - printer->Outdent(); - printer->Print(Vars, "}\n\n"); - } - -public: - explicit TMessageGenerator(const Descriptor* message, OutputDirectory* output) - : Message(message) - , Output(output) - , Header(output, HeaderFileName(message), ClassScope(message)) - , Source(output, SourceFileName(message), NamespaceScope()) - , Vars({ - {"class", ClassName(message)}, - }) - { - } - - void Generate() { - for (auto i = 0; i < Message->nested_type_count(); ++i) { - const Descriptor* message = Message->nested_type(i); - if (!IsCustomMessage(message)) { - continue; - } - - TMessageGenerator mg(message, Output); - mg.Generate(); - } - - TItems items; - - for (auto i = 0; i < Message->field_count(); ++i) { - const FieldDescriptor* field = Message->field(i); - const OneofDescriptor* oneof = field->containing_oneof(); - - TFieldGenerator::TPtr fieldGen = new TFieldGenerator(field, Vars.at("class")); - TOneofGenerator::TPtr oneofGen = oneof ? new TOneofGenerator(oneof, Vars.at("class")) : nullptr; - - auto it = items.find(oneof); - if (it == items.end()) { - it = items.emplace(oneof, TItem(oneofGen)).first; - } - - it->second.AddField(fieldGen); - } - - for (const auto& [_, item] : items) { - for (const auto& field : item.Fields) { - field->Declare(Header); - field->Define(Source); - } - - if (item.Oneof) { - item.Oneof->Declare(Header); - item.Oneof->Define(Source); - } - } - - Declare(Header, items); - Define(Source, items); - } - -private: - const Descriptor* Message; - OutputDirectory* Output; - TPrinter Header; - TPrinter Source; - const TVariables Vars; - -}; // TMessageGenerator - -class TCodeGenerator: public CodeGenerator { - bool Generate( - const FileDescriptor* file, - const TProtoStringType&, - OutputDirectory* output, - TProtoStringType*) const override final { - - for (auto i = 0; i < file->message_type_count(); ++i) { - TMessageGenerator mg(file->message_type(i), output); - mg.Generate(); - } - - return true; - } - -}; // TCodeGenerator - -} // NValidation -} // NKikimr - -int main(int argc, char* argv[]) { - NKikimr::NValidation::TCodeGenerator generator; - return google::protobuf::compiler::PluginMain(argc, argv, &generator); -} diff --git a/ydb/core/grpc_services/validation/ut.cpp b/ydb/core/grpc_services/validation/ut.cpp deleted file mode 100644 index ca414df810f..00000000000 --- a/ydb/core/grpc_services/validation/ut.cpp +++ /dev/null @@ -1,288 +0,0 @@ -#include - -#include - -Y_UNIT_TEST_SUITE(ValidationTests) { - using namespace Ydb::ValidationTest; - - template - bool Validate(const T& proto, TString& error) { - return proto.validate(error); - } - - template - bool Validate(const T& proto) { - TString unused; - return Validate(proto, unused); - } - - template - void Validate(const T& proto, const TString& errorContains) { - TString error; - UNIT_ASSERT(!Validate(proto, error)); - UNIT_ASSERT_STRING_CONTAINS(error, errorContains); - } - - Y_UNIT_TEST(Empty) { - UNIT_ASSERT(Validate(EmptyMessage())); - } - - Y_UNIT_TEST(WithouValidators) { - UNIT_ASSERT(Validate(MessageWithoutValidators())); - } - - Y_UNIT_TEST(Basic) { - MessageBasic basic; - - // required - { - Validate(basic, "required but not set"); - basic.set_check_required("set"); - } - - // size: range - { - Validate(basic, "size is not in"); - basic.mutable_check_size_range_1_2()->Add("string"); - basic.mutable_check_size_range_1_2()->Add("string"); - basic.mutable_check_size_range_1_2()->Add("string"); // extra - - Validate(basic, "size is not in"); - basic.mutable_check_size_range_1_2()->RemoveLast(); - } - - // size: lt - { - basic.mutable_check_size_lt_2()->Add("1"); - basic.mutable_check_size_lt_2()->Add("2"); // extra - Validate(basic, "size is not <"); - basic.mutable_check_size_lt_2()->RemoveLast(); - } - - // size: le - { - basic.mutable_check_size_le_1()->Add("1"); - basic.mutable_check_size_le_1()->Add("2"); // extra - Validate(basic, "size is not <="); - basic.mutable_check_size_le_1()->RemoveLast(); - } - - // size: eq - { - basic.mutable_check_size_eq_1()->Add("1"); - basic.mutable_check_size_eq_1()->Add("2"); // extra - Validate(basic, "size is not =="); - basic.mutable_check_size_eq_1()->RemoveLast(); - } - - // size: ge - { - Validate(basic, "size is not >="); - basic.mutable_check_size_ge_1()->Add("1"); - } - - // size: gt - { - basic.mutable_check_size_gt_1()->Add("1"); - Validate(basic, "size is not >"); - basic.mutable_check_size_gt_1()->Add("1"); - } - - // length: range - { - Validate(basic, "length is not in"); - basic.set_check_length_range_1_3("value"); - - Validate(basic, "length is not in"); - basic.set_check_length_range_1_3("val"); - } - - // length: lt - { - basic.set_check_length_lt_4("value"); - Validate(basic, "length is not <"); - basic.set_check_length_lt_4("val"); - } - - // length: le - { - basic.set_check_length_le_3("value"); - Validate(basic, "length is not <="); - basic.set_check_length_le_3("val"); - } - - // length: eq - { - basic.set_check_length_eq_3("value"); - Validate(basic, "length is not =="); - basic.set_check_length_eq_3("val"); - } - - // length: ge - { - basic.set_check_length_ge_3("v"); - Validate(basic, "length is not >="); - basic.set_check_length_ge_3("val"); - } - - // length: gt - { - basic.set_check_length_gt_3("val"); - Validate(basic, "length is not >"); - basic.set_check_length_gt_3("value"); - } - - // value: int32 - { - basic.set_check_value_int32(-1); - Validate(basic, "value is not"); - basic.set_check_value_int32(-2); - } - - // value: int64 - { - basic.set_check_value_int64(-5'000'000'000); - Validate(basic, "value is not"); - basic.set_check_value_int64(-5'000'000'001); - } - - // value: uint32 - { - basic.set_check_value_uint32(1); - Validate(basic, "value is not"); - basic.set_check_value_uint32(2); - } - - // value: uint64 - { - basic.set_check_value_uint64(5'000'000'000); - Validate(basic, "value is not"); - basic.set_check_value_uint64(5'000'000'001); - } - - // value: double - { - basic.set_check_value_double(0.99); - Validate(basic, "value is not"); - basic.set_check_value_double(1.01); - } - - // value: float - { - basic.set_check_value_float(-0.99); - Validate(basic, "value is not"); - basic.set_check_value_float(-1.01); - } - - // value: bool - { - basic.set_check_value_bool(false); - Validate(basic, "value is not"); - basic.set_check_value_bool(true); - } - - // value: string - { - basic.set_check_value_string("changeme"); - Validate(basic, "value is not"); - basic.set_check_value_string("changed"); - } - - // value: range [1;10] - { - basic.set_check_value_range_1(11); - Validate(basic, "value is not"); - basic.set_check_value_range_1(10); - } - - // value: range (1;10] - { - basic.set_check_value_range_2(1); - Validate(basic, "value is not"); - basic.set_check_value_range_2(2); - } - - // value: range [1;10) - { - basic.set_check_value_range_3(10); - Validate(basic, "value is not"); - basic.set_check_value_range_3(9); - } - - // value: range (1;10) - { - basic.set_check_value_range_4(1); - Validate(basic, "value is not"); - basic.set_check_value_range_4(5); - } - - UNIT_ASSERT(Validate(basic)); - } - - Y_UNIT_TEST(Map) { - MessageMap map; - - // size - { - Validate(map, "size is not >"); - map.mutable_values()->insert({"key", "value"}); - } - - // value's length - { - Validate(map, "value length is not <"); - map.mutable_values()->at("key") = "v"; - } - - // key's length - { - Validate(map, "key length is not <"); - map.mutable_values()->erase("key"); - map.mutable_values()->insert({"k", "v"}); - } - - // key - { - Validate(map, "key is not"); - map.mutable_values()->erase("k"); - map.mutable_values()->insert({"a", "v"}); - } - - // value - { - Validate(map, "value is not"); - map.mutable_values()->erase("a"); - map.mutable_values()->insert({"a", "b"}); - } - - UNIT_ASSERT(Validate(map)); - } - - Y_UNIT_TEST(Oneof) { - MessageOneof oneof; - - // required - { - Validate(oneof, "required but not set"); - oneof.set_check_required("set"); - } - - // check oneof (1) - { - oneof.set_check_le_1("12"); - Validate(oneof, "length is not <="); - - oneof.set_check_le_1("1"); - UNIT_ASSERT(Validate(oneof)); - } - - // check oneof (2) - { - oneof.set_check_le_2("123"); - Validate(oneof, "length is not <="); - - oneof.set_check_le_2("12"); - UNIT_ASSERT(Validate(oneof)); - } - } -} diff --git a/ydb/core/grpc_services/validation/ut/CMakeLists.darwin.txt b/ydb/core/grpc_services/validation/ut/CMakeLists.darwin.txt deleted file mode 100644 index 387c901012c..00000000000 --- a/ydb/core/grpc_services/validation/ut/CMakeLists.darwin.txt +++ /dev/null @@ -1,43 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - - -add_executable(ydb-core-grpc_services-validation-ut) -target_include_directories(ydb-core-grpc_services-validation-ut PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation -) -target_link_libraries(ydb-core-grpc_services-validation-ut PUBLIC - contrib-libs-cxxsupp - yutil - library-cpp-cpuid_check - cpp-testing-unittest_main - cpp-testing-unittest - validation-ut-protos -) -target_sources(ydb-core-grpc_services-validation-ut PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/ut.cpp -) -add_test( - NAME - ydb-core-grpc_services-validation-ut - COMMAND - ydb-core-grpc_services-validation-ut - --print-before-suite - --print-before-test - --fork-tests - --print-times - --show-fails -) -target_link_flags(ydb-core-grpc_services-validation-ut - PUBLIC - -Wl,-no_deduplicate - -Wl,-sdk_version,10.15 - -fPIC - -fPIC -) -vcs_info(ydb-core-grpc_services-validation-ut) diff --git a/ydb/core/grpc_services/validation/ut/CMakeLists.linux.txt b/ydb/core/grpc_services/validation/ut/CMakeLists.linux.txt deleted file mode 100644 index 70065eda7ab..00000000000 --- a/ydb/core/grpc_services/validation/ut/CMakeLists.linux.txt +++ /dev/null @@ -1,48 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - - -add_executable(ydb-core-grpc_services-validation-ut) -target_include_directories(ydb-core-grpc_services-validation-ut PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation -) -target_link_libraries(ydb-core-grpc_services-validation-ut PUBLIC - contrib-libs-cxxsupp - yutil - library-cpp-lfalloc - library-cpp-cpuid_check - cpp-testing-unittest_main - cpp-testing-unittest - validation-ut-protos -) -target_sources(ydb-core-grpc_services-validation-ut PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/ut.cpp -) -add_test( - NAME - ydb-core-grpc_services-validation-ut - COMMAND - ydb-core-grpc_services-validation-ut - --print-before-suite - --print-before-test - --fork-tests - --print-times - --show-fails -) -target_link_flags(ydb-core-grpc_services-validation-ut - PUBLIC - -ldl - -lrt - -Wl,--no-as-needed - -fPIC - -fPIC - -lpthread - -lrt - -ldl -) -vcs_info(ydb-core-grpc_services-validation-ut) diff --git a/ydb/core/grpc_services/validation/ut/CMakeLists.txt b/ydb/core/grpc_services/validation/ut/CMakeLists.txt deleted file mode 100644 index a681d385f3e..00000000000 --- a/ydb/core/grpc_services/validation/ut/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - -if (APPLE) - include(CMakeLists.darwin.txt) -elseif (UNIX) - include(CMakeLists.linux.txt) -endif() diff --git a/ydb/core/grpc_services/validation/ut/protos/CMakeLists.txt b/ydb/core/grpc_services/validation/ut/protos/CMakeLists.txt deleted file mode 100644 index 5130b88a51e..00000000000 --- a/ydb/core/grpc_services/validation/ut/protos/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ - -# This file was gererated by the build system used internally in the Yandex monorepo. -# Only simple modifications are allowed (adding source-files to targets, adding simple properties -# like target_include_directories). These modifications will be ported to original -# ya.make files by maintainers. Any complex modifications which can't be ported back to the -# original buildsystem will not be accepted. - - - -add_library(validation-ut-protos) -target_link_libraries(validation-ut-protos PUBLIC - contrib-libs-cxxsupp - yutil - api-protos-annotations - contrib-libs-protobuf -) -target_proto_messages(validation-ut-protos PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/grpc_services/validation/ut/protos/validation_test.proto -) -target_proto_plugin(validation-ut-protos - validation - validation -) diff --git a/ydb/core/grpc_services/validation/ut/protos/validation_test.proto b/ydb/core/grpc_services/validation/ut/protos/validation_test.proto deleted file mode 100644 index f2c26ab3887..00000000000 --- a/ydb/core/grpc_services/validation/ut/protos/validation_test.proto +++ /dev/null @@ -1,60 +0,0 @@ -syntax = "proto3"; -option cc_enable_arenas = true; - -import "ydb/public/api/protos/annotations/validation.proto"; - -package Ydb.ValidationTest; - -message EmptyMessage { -} - -message MessageWithoutValidators { - uint32 key = 1; - string value = 2; - repeated string payload = 3; -} - -message MessageBasic { - string check_required = 1 [(required) = true]; - - repeated string check_size_range_1_2 = 2 [(size).range = {min: 1, max: 2}]; - repeated string check_size_lt_2 = 3 [(size).lt = 2]; - repeated string check_size_le_1 = 4 [(size).le = 1]; - repeated string check_size_eq_1 = 5 [(size).eq = 1]; - repeated string check_size_ge_1 = 6 [(size).ge = 1]; - repeated string check_size_gt_1 = 7 [(size).gt = 1]; - - string check_length_range_1_3 = 8 [(length).range = {min: 1, max: 3}]; - string check_length_lt_4 = 9 [(length).lt = 4]; - string check_length_le_3 = 10 [(length).le = 3]; - string check_length_eq_3 = 11 [(length).eq = 3]; - string check_length_ge_3 = 12 [(length).ge = 3]; - string check_length_gt_3 = 13 [(length).gt = 3]; - - int32 check_value_int32 = 14 [(value) = "< -1"]; - int64 check_value_int64 = 15 [(value) = "< -5'000'000'000"]; - uint32 check_value_uint32 = 16 [(value) = "> 1"]; - uint64 check_value_uint64 = 17 [(value) = "> 5'000'000'000"]; - double check_value_double = 18 [(value) = "> 1.0"]; - float check_value_float = 19 [(value) = "< -1.0"]; - bool check_value_bool = 20 [(value) = "== true"]; - string check_value_string = 21 [(value) = '!= "changeme"']; - - int32 check_value_range_1 = 22 [(value) = "[1; 10]"]; - int32 check_value_range_2 = 23 [(value) = "(1; 10]"]; - int32 check_value_range_3 = 24 [(value) = "[1; 10)"]; - int32 check_value_range_4 = 25 [(value) = "(1; 10)"]; -} - -message MessageMap { - map values = 1 [(size).gt = 0, (map_key).length.lt = 2, (length).lt = 2, (map_key).value = '!= "k"', (value) = '!= "v"']; -} - -message MessageOneof { - uint32 key = 1; - string check_required = 2 [(required) = true]; - oneof value { - string check_le_1 = 3 [(length).le = 1]; - string check_le_2 = 4 [(length).le = 2]; - } -} diff --git a/ydb/public/lib/validation/CMakeLists.darwin.txt b/ydb/public/lib/validation/CMakeLists.darwin.txt new file mode 100644 index 00000000000..dee562c94c4 --- /dev/null +++ b/ydb/public/lib/validation/CMakeLists.darwin.txt @@ -0,0 +1,29 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_executable(validation) +target_link_libraries(validation PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + contrib-libs-protoc + api-protos-annotations +) +target_sources(validation PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/helpers.cpp + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/main.cpp +) +target_link_flags(validation + PUBLIC + -Wl,-no_deduplicate + -Wl,-sdk_version,10.15 + -fPIC + -fPIC +) +vcs_info(validation) diff --git a/ydb/public/lib/validation/CMakeLists.linux.txt b/ydb/public/lib/validation/CMakeLists.linux.txt new file mode 100644 index 00000000000..2fe1e25a376 --- /dev/null +++ b/ydb/public/lib/validation/CMakeLists.linux.txt @@ -0,0 +1,34 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_executable(validation) +target_link_libraries(validation PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-lfalloc + library-cpp-cpuid_check + contrib-libs-protoc + api-protos-annotations +) +target_sources(validation PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/helpers.cpp + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/main.cpp +) +target_link_flags(validation + PUBLIC + -ldl + -lrt + -Wl,--no-as-needed + -fPIC + -fPIC + -lpthread + -lrt + -ldl +) +vcs_info(validation) diff --git a/ydb/public/lib/validation/CMakeLists.txt b/ydb/public/lib/validation/CMakeLists.txt new file mode 100644 index 00000000000..a681d385f3e --- /dev/null +++ b/ydb/public/lib/validation/CMakeLists.txt @@ -0,0 +1,13 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (APPLE) + include(CMakeLists.darwin.txt) +elseif (UNIX) + include(CMakeLists.linux.txt) +endif() diff --git a/ydb/public/lib/validation/helpers.cpp b/ydb/public/lib/validation/helpers.cpp new file mode 100644 index 00000000000..0b30fdcec79 --- /dev/null +++ b/ydb/public/lib/validation/helpers.cpp @@ -0,0 +1,66 @@ +#include "helpers.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace NKikimr { +namespace NValidation { + +static TString ProtoFileNameStripped(const google::protobuf::Descriptor* message) { + return google::protobuf::compiler::cpp::StripProto(message->file()->name()); +} + +TString HeaderFileName(const google::protobuf::Descriptor* message) { + return ProtoFileNameStripped(message).append(".pb.h"); +} + +TString SourceFileName(const google::protobuf::Descriptor* message) { + return ProtoFileNameStripped(message).append(".pb.cc"); +} + +TString ClassScope(const google::protobuf::Descriptor* message) { + return "class_scope:" + message->full_name(); +} + +TString NamespaceScope() { + return "namespace_scope"; +} + +TString ClassName(const google::protobuf::Descriptor* message) { + const TString ns = message->file()->package(); + TString className = !ns.empty() ? message->full_name().substr(ns.size() + 1) : message->full_name(); + SubstGlobal(className, ".", "::"); + return className; +} + +bool IsCustomMessage(const google::protobuf::Descriptor* message) { + if (!message) { + return false; + } + if (message->full_name() == google::protobuf::Any::descriptor()->full_name()) { + return false; + } + if (message->full_name() == google::protobuf::Duration::descriptor()->full_name()) { + return false; + } + if (message->full_name() == google::protobuf::Empty::descriptor()->full_name()) { + return false; + } + if (message->full_name() == google::protobuf::Timestamp::descriptor()->full_name()) { + return false; + } + if (message->options().map_entry()) { + return false; + } + + return true; +} + +} // NValidation +} // NKikimr diff --git a/ydb/public/lib/validation/helpers.h b/ydb/public/lib/validation/helpers.h new file mode 100644 index 00000000000..60047a9dd78 --- /dev/null +++ b/ydb/public/lib/validation/helpers.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace NKikimr { +namespace NValidation { + +TString HeaderFileName(const google::protobuf::Descriptor* message); +TString SourceFileName(const google::protobuf::Descriptor* message); +TString ClassScope(const google::protobuf::Descriptor* message); +TString NamespaceScope(); +TString ClassName(const google::protobuf::Descriptor* message); + +bool IsCustomMessage(const google::protobuf::Descriptor* message); + +} // NValidation +} // NKikimr diff --git a/ydb/public/lib/validation/main.cpp b/ydb/public/lib/validation/main.cpp new file mode 100644 index 00000000000..68c4bda65cb --- /dev/null +++ b/ydb/public/lib/validation/main.cpp @@ -0,0 +1,581 @@ +#include "helpers.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace NKikimr { +namespace NValidation { + +using namespace google::protobuf::compiler; +using namespace google::protobuf; + +using TVariables = std::map; + +class TPrinter { +public: + explicit TPrinter(OutputDirectory* output, const TString& fileName, const TString& scope) + : Output(output) + , FileName(fileName) + , Scope(scope) + { + } + + io::Printer* operator->() { + if (!Printer) { + Stream.Reset(Output->OpenForInsert(FileName, Scope)); + Printer.ConstructInPlace(Stream.Get(), '$'); + } + + return Printer.Get(); + } + +private: + OutputDirectory* Output; + const TString FileName; + const TString Scope; + + THolder Stream; + TMaybe Printer; + +}; // TPrinter + +class TFieldGenerator: public TThrRefBase { + void Required(TPrinter& printer) const { + Y_VERIFY(!Field->is_repeated(), "Repeated fields cannot be required or not"); + + if (Field->options().GetExtension(Ydb::required)) { + if (Field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + printer->Print(Vars, "if ($field$().empty()) {\n"); + } else if (Field->message_type()) { + printer->Print(Vars, "if (!has_$field$()) {\n"); + } else { + Y_FAIL_S(" Field type = " << Field->type_name() << " cannot be required or not"); + } + + printer->Indent(); + printer->Print(Vars, "__err = \"$field$ is required but not set\";\n"); + printer->Print(Vars, "return false;\n"); + printer->Outdent(); + printer->Print(Vars, "}\n"); + } + } + + void CheckLimit(TPrinter& printer, const Ydb::Limit& limit, TVariables vars) const { + switch (limit.kind_case()) { + case Ydb::Limit::kRange: + vars["min"] = ToString(limit.range().min()); + vars["max"] = ToString(limit.range().max()); + printer->Print(vars, "if (!($min$ <= $getter$ && $getter$ <= $max$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not in [$min$; $max$]\";\n"); + break; + + case Ydb::Limit::kLt: + vars["lt"] = ToString(limit.lt()); + printer->Print(vars, "if (!($getter$ < $lt$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not < $lt$\";\n"); + break; + + case Ydb::Limit::kLe: + vars["le"] = ToString(limit.le()); + printer->Print(vars, "if (!($getter$ <= $le$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not <= $le$\";\n"); + break; + + case Ydb::Limit::kEq: + vars["eq"] = ToString(limit.eq()); + printer->Print(vars, "if (!($getter$ == $eq$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not == $eq$\";\n"); + break; + + case Ydb::Limit::kGe: + vars["ge"] = ToString(limit.ge()); + printer->Print(vars, "if (!($getter$ >= $ge$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not >= $ge$\";\n"); + break; + + case Ydb::Limit::kGt: + vars["gt"] = ToString(limit.gt()); + printer->Print(vars, "if (!($getter$ > $gt$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not > $gt$\";\n"); + break; + + default: + Y_FAIL("Unknown limit type"); + } + + printer->Print(vars, "return false;\n"); + printer->Outdent(); + printer->Print(vars, "}\n"); + } + + static TString BuildValueChecker(const TString& getter, TStringBuf annValue) { + if (annValue.Contains('<') || annValue.Contains('>') || annValue.Contains('=')) { + return TStringBuilder() << getter << " " << annValue; + } + + if ((annValue.StartsWith('[') || annValue.StartsWith('(')) + && (annValue.EndsWith(']') || annValue.EndsWith(')')) + && annValue.Contains(';')) { + + const bool leftInclusive = annValue.StartsWith('['); + const bool rightInclusive = annValue.EndsWith(']'); + + annValue.Skip(1).Chop(1); + + const TStringBuf left = annValue.Before(';'); + const TStringBuf right = annValue.After(';'); + + return TStringBuilder() + << left << " " << (leftInclusive ? "<=" : "<") + << " " << getter << " && " << getter << " " + << (rightInclusive ? "<=" : "<") << " " << right; + } + + Y_FAIL_S("Invalid value: " << annValue); + } + + void CheckValue(TPrinter& printer, const FieldDescriptor* field, TVariables vars) const { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_UINT64: + case FieldDescriptor::CPPTYPE_DOUBLE: + case FieldDescriptor::CPPTYPE_FLOAT: + case FieldDescriptor::CPPTYPE_BOOL: + case FieldDescriptor::CPPTYPE_STRING: + printer->Print(vars, "if (!($value_checker$)) {\n"); + printer->Indent(); + printer->Print(vars, "__err = \"$field$$kind$ is not $value$\";\n"); + printer->Print(vars, "return false;\n"); + printer->Outdent(); + printer->Print(vars, "}\n"); + break; + + default: + Y_FAIL_S("Cannot check value of field: type = " << field->type_name()); + } + } + + void Size(TPrinter& printer) const { + Y_VERIFY(Field->is_repeated(), "Cannot check size of non-repeated field"); + + TVariables vars = Vars; + vars["kind"] = " size"; + vars["getter"] = Field->name() + "_size()"; + + CheckLimit(printer, Field->options().GetExtension(Ydb::size), vars); + } + + void Length(TPrinter& printer) const { + if (Field->is_repeated()) { + printer->Print(Vars, "for (const auto& value : $field$()) {\n"); + printer->Indent(); + } + + if (Field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + TVariables vars = Vars; + vars["kind"] = "'s length"; + vars["getter"] = Field->is_repeated() ? "value.size()" : Field->name() + "().size()"; + + CheckLimit(printer, Field->options().GetExtension(Ydb::length), vars); + } else if (Field->is_map()) { + const FieldDescriptor* value = Field->message_type()->FindFieldByName("value"); + Y_VERIFY_S(value->cpp_type() == FieldDescriptor::CPPTYPE_STRING, "Cannot check length of field: type = " << value->type_name()); + + TVariables vars = Vars; + vars["kind"] = "'s value length"; + vars["getter"] = "value.second.size()"; + + CheckLimit(printer, Field->options().GetExtension(Ydb::length), vars); + } else { + Y_FAIL_S("Cannot check length of field: type = " << Field->type_name()); + } + + if (Field->is_repeated()) { + printer->Outdent(); + printer->Print(Vars, "}\n"); + } + } + + void MapKey(TPrinter& printer) const { + Y_VERIFY(Field->is_map(), "Cannot validate map key of non-map field"); + + printer->Print(Vars, "for (const auto& value : $field$()) {\n"); + printer->Indent(); + + TVariables vars = Vars; + + const auto& mapKey = Field->options().GetExtension(Ydb::map_key); + if (mapKey.has_length()) { + const FieldDescriptor* key = Field->message_type()->FindFieldByName("key"); + Y_VERIFY_S(key->cpp_type() == FieldDescriptor::CPPTYPE_STRING, "Cannot check length of field: type = " << key->type_name()); + + vars["kind"] = "'s key length"; + vars["getter"] = "value.first.size()"; + CheckLimit(printer, mapKey.length(), vars); + } + if (mapKey.value()) { + vars["kind"] = "'s key"; + vars["value"] = SubstGlobalCopy(mapKey.value(), '"', '\''); + vars["value_checker"] = BuildValueChecker("value.first", mapKey.value()); + CheckValue(printer, Field->message_type()->FindFieldByName("key"), vars); + } + + printer->Outdent(); + printer->Print(Vars, "}\n"); + } + + void Value(TPrinter& printer) const { + if (Field->is_repeated()) { + printer->Print(Vars, "for (const auto& value : $field$()) {\n"); + printer->Indent(); + } + + const TString value = Field->options().GetExtension(Ydb::value); + + TVariables vars = Vars; + vars["kind"] = "'s value"; + vars["value"] = SubstGlobalCopy(value, '"', '\''); + + if (Field->is_map()) { + vars["value_checker"] = BuildValueChecker("value.second", value); + CheckValue(printer, Field->message_type()->FindFieldByName("value"), vars); + } else { + vars["value_checker"] = BuildValueChecker(Field->is_repeated() ? "value" : Field->name() + "()", value); + CheckValue(printer, Field, vars); + } + + if (Field->is_repeated()) { + printer->Outdent(); + printer->Print(Vars, "}\n"); + } + } + + void Body(TPrinter& printer) const { + const auto& opts = Field->options(); + + if (opts.HasExtension(Ydb::required)) { + Required(printer); + } + if (opts.HasExtension(Ydb::size)) { + Size(printer); + } + if (opts.HasExtension(Ydb::length)) { + Length(printer); + } + if (opts.HasExtension(Ydb::map_key)) { + MapKey(printer); + } + if (opts.HasExtension(Ydb::value)) { + Value(printer); + } + + if (IsCustomMessage(Field->message_type())) { + if (Field->is_repeated()) { + printer->Print(Vars, "for (const auto& value : $field$()) {\n"); + printer->Indent(); + printer->Print(Vars, "if (!value.validate(__err)) {\n"); + printer->Indent(); + printer->Print(Vars, "return false;\n"); + printer->Outdent(); + printer->Print(Vars, "}\n"); + printer->Outdent(); + printer->Print(Vars, "}\n"); + printer->Print(Vars, "\nreturn true;\n"); + } else { + printer->Print(Vars, "return $field$().validate(__err);\n"); + } + } else { + printer->Print(Vars, "return true;\n"); + } + } + + bool HasValidators() const { + const auto& opts = Field->options(); + return opts.HasExtension(Ydb::required) + || opts.HasExtension(Ydb::size) + || opts.HasExtension(Ydb::length) + || opts.HasExtension(Ydb::map_key) + || opts.HasExtension(Ydb::value); + } + + static TString PascalName(TString camelName) { + camelName.to_upper(0, 1); + return camelName; + } + +public: + using TPtr = TIntrusivePtr; + + explicit TFieldGenerator(const FieldDescriptor* field, const TString& className) + : Field(field) + , Vars({ + {"class", className}, + {"func", "validate_" + field->name()}, + {"field", field->name()}, + {"PascalName", PascalName(field->camelcase_name())}, + }) + { + } + + const TVariables& GetVars() const { + return Vars; + } + + void Declare(TPrinter& printer) const { + if (!HasValidators() && !IsCustomMessage(Field->message_type())) { + printer->Print(Vars, "bool $func$(TProtoStringType&) const { return true; }\n"); + } else { + printer->Print(Vars, "bool $func$(TProtoStringType& __err) const;\n"); + } + } + + void Define(TPrinter& printer) const { + if (!HasValidators() && !IsCustomMessage(Field->message_type())) { + return; + } + + printer->Print(Vars, "bool $class$::$func$(TProtoStringType& __err) const {\n"); + printer->Indent(); + Body(printer); + printer->Outdent(); + printer->Print(Vars, "}\n\n"); + } + +private: + const FieldDescriptor* Field; + const TVariables Vars; + +}; // TFieldGenerator + +class TOneofGenerator: public TThrRefBase { + void Body(TPrinter& printer) const { + printer->Print(Vars, "switch ($oneof$_case()) {\n"); + + for (auto field : Fields) { + printer->Print(field->GetVars(), "case k$PascalName$:\n"); + printer->Indent(); + printer->Print(field->GetVars(), "return $func$(__err);\n"); + printer->Outdent(); + } + + printer->Print(Vars, "default:\n"); + printer->Indent(); + printer->Print(Vars, "return true;\n"); + printer->Outdent(); + + printer->Print(Vars, "}\n"); + } + +public: + using TPtr = TIntrusivePtr; + + explicit TOneofGenerator(const OneofDescriptor* oneof, const TString& className) + : Vars({ + {"class", className}, + {"oneof", oneof->name()}, + {"func", "validate_" + oneof->name()}, + }) + { + } + + const TVariables& GetVars() const { + return Vars; + } + + void AddField(TFieldGenerator::TPtr field) { + Fields.push_back(field); + } + + void Declare(TPrinter& printer) const { + printer->Print(Vars, "bool $func$(TProtoStringType& __err) const;\n"); + } + + void Define(TPrinter& printer) const { + printer->Print(Vars, "bool $class$::$func$(TProtoStringType& __err) const {\n"); + printer->Indent(); + Body(printer); + printer->Outdent(); + printer->Print(Vars, "}\n\n"); + } + +private: + const TVariables Vars; + TVector Fields; + +}; // TOneofGenerator + +class TMessageGenerator { + struct TItem { + TOneofGenerator::TPtr Oneof; + TVector Fields; + + explicit TItem(TOneofGenerator::TPtr oneof) + : Oneof(oneof) + { + } + + void AddField(TFieldGenerator::TPtr field) { + Fields.push_back(field); + if (Oneof) { + Oneof->AddField(field); + } + } + }; + + using TItems = THashMap; + + void Declare(TPrinter& printer, const TItems& items) { + if (!items) { + printer->Print(Vars, "bool validate(TProtoStringType&) const { return true; }\n"); + } else { + printer->Print(Vars, "bool validate(TProtoStringType& __err) const;\n"); + } + } + + void Define(TPrinter& printer, const TItems& items) { + if (!items) { + return; + } + + printer->Print(Vars, "bool $class$::validate(TProtoStringType& __err) const {\n"); + printer->Indent(); + printer->Print(Vars, "return\n"); + printer->Indent(); + + bool first = true; + for (const auto& [_, item] : items) { + if (!item.Oneof) { + for (const auto& field : item.Fields) { + if (first) { + first = false; + } else { + printer->Print(Vars, "&& "); + } + printer->Print(field->GetVars(), "$func$(__err)\n"); + } + } else { + if (first) { + first = false; + } else { + printer->Print(Vars, "&& "); + } + printer->Print(item.Oneof->GetVars(), "$func$(__err)\n"); + } + } + + printer->Outdent(); + printer->Print(Vars, ";\n"); + printer->Outdent(); + printer->Print(Vars, "}\n\n"); + } + +public: + explicit TMessageGenerator(const Descriptor* message, OutputDirectory* output) + : Message(message) + , Output(output) + , Header(output, HeaderFileName(message), ClassScope(message)) + , Source(output, SourceFileName(message), NamespaceScope()) + , Vars({ + {"class", ClassName(message)}, + }) + { + } + + void Generate() { + for (auto i = 0; i < Message->nested_type_count(); ++i) { + const Descriptor* message = Message->nested_type(i); + if (!IsCustomMessage(message)) { + continue; + } + + TMessageGenerator mg(message, Output); + mg.Generate(); + } + + TItems items; + + for (auto i = 0; i < Message->field_count(); ++i) { + const FieldDescriptor* field = Message->field(i); + const OneofDescriptor* oneof = field->containing_oneof(); + + TFieldGenerator::TPtr fieldGen = new TFieldGenerator(field, Vars.at("class")); + TOneofGenerator::TPtr oneofGen = oneof ? new TOneofGenerator(oneof, Vars.at("class")) : nullptr; + + auto it = items.find(oneof); + if (it == items.end()) { + it = items.emplace(oneof, TItem(oneofGen)).first; + } + + it->second.AddField(fieldGen); + } + + for (const auto& [_, item] : items) { + for (const auto& field : item.Fields) { + field->Declare(Header); + field->Define(Source); + } + + if (item.Oneof) { + item.Oneof->Declare(Header); + item.Oneof->Define(Source); + } + } + + Declare(Header, items); + Define(Source, items); + } + +private: + const Descriptor* Message; + OutputDirectory* Output; + TPrinter Header; + TPrinter Source; + const TVariables Vars; + +}; // TMessageGenerator + +class TCodeGenerator: public CodeGenerator { + bool Generate( + const FileDescriptor* file, + const TProtoStringType&, + OutputDirectory* output, + TProtoStringType*) const override final { + + for (auto i = 0; i < file->message_type_count(); ++i) { + TMessageGenerator mg(file->message_type(i), output); + mg.Generate(); + } + + return true; + } + +}; // TCodeGenerator + +} // NValidation +} // NKikimr + +int main(int argc, char* argv[]) { + NKikimr::NValidation::TCodeGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/ydb/public/lib/validation/ut.cpp b/ydb/public/lib/validation/ut.cpp new file mode 100644 index 00000000000..ae736bf895a --- /dev/null +++ b/ydb/public/lib/validation/ut.cpp @@ -0,0 +1,288 @@ +#include + +#include + +Y_UNIT_TEST_SUITE(ValidationTests) { + using namespace Ydb::ValidationTest; + + template + bool Validate(const T& proto, TString& error) { + return proto.validate(error); + } + + template + bool Validate(const T& proto) { + TString unused; + return Validate(proto, unused); + } + + template + void Validate(const T& proto, const TString& errorContains) { + TString error; + UNIT_ASSERT(!Validate(proto, error)); + UNIT_ASSERT_STRING_CONTAINS(error, errorContains); + } + + Y_UNIT_TEST(Empty) { + UNIT_ASSERT(Validate(EmptyMessage())); + } + + Y_UNIT_TEST(WithouValidators) { + UNIT_ASSERT(Validate(MessageWithoutValidators())); + } + + Y_UNIT_TEST(Basic) { + MessageBasic basic; + + // required + { + Validate(basic, "required but not set"); + basic.set_check_required("set"); + } + + // size: range + { + Validate(basic, "size is not in"); + basic.mutable_check_size_range_1_2()->Add("string"); + basic.mutable_check_size_range_1_2()->Add("string"); + basic.mutable_check_size_range_1_2()->Add("string"); // extra + + Validate(basic, "size is not in"); + basic.mutable_check_size_range_1_2()->RemoveLast(); + } + + // size: lt + { + basic.mutable_check_size_lt_2()->Add("1"); + basic.mutable_check_size_lt_2()->Add("2"); // extra + Validate(basic, "size is not <"); + basic.mutable_check_size_lt_2()->RemoveLast(); + } + + // size: le + { + basic.mutable_check_size_le_1()->Add("1"); + basic.mutable_check_size_le_1()->Add("2"); // extra + Validate(basic, "size is not <="); + basic.mutable_check_size_le_1()->RemoveLast(); + } + + // size: eq + { + basic.mutable_check_size_eq_1()->Add("1"); + basic.mutable_check_size_eq_1()->Add("2"); // extra + Validate(basic, "size is not =="); + basic.mutable_check_size_eq_1()->RemoveLast(); + } + + // size: ge + { + Validate(basic, "size is not >="); + basic.mutable_check_size_ge_1()->Add("1"); + } + + // size: gt + { + basic.mutable_check_size_gt_1()->Add("1"); + Validate(basic, "size is not >"); + basic.mutable_check_size_gt_1()->Add("1"); + } + + // length: range + { + Validate(basic, "length is not in"); + basic.set_check_length_range_1_3("value"); + + Validate(basic, "length is not in"); + basic.set_check_length_range_1_3("val"); + } + + // length: lt + { + basic.set_check_length_lt_4("value"); + Validate(basic, "length is not <"); + basic.set_check_length_lt_4("val"); + } + + // length: le + { + basic.set_check_length_le_3("value"); + Validate(basic, "length is not <="); + basic.set_check_length_le_3("val"); + } + + // length: eq + { + basic.set_check_length_eq_3("value"); + Validate(basic, "length is not =="); + basic.set_check_length_eq_3("val"); + } + + // length: ge + { + basic.set_check_length_ge_3("v"); + Validate(basic, "length is not >="); + basic.set_check_length_ge_3("val"); + } + + // length: gt + { + basic.set_check_length_gt_3("val"); + Validate(basic, "length is not >"); + basic.set_check_length_gt_3("value"); + } + + // value: int32 + { + basic.set_check_value_int32(-1); + Validate(basic, "value is not"); + basic.set_check_value_int32(-2); + } + + // value: int64 + { + basic.set_check_value_int64(-5'000'000'000); + Validate(basic, "value is not"); + basic.set_check_value_int64(-5'000'000'001); + } + + // value: uint32 + { + basic.set_check_value_uint32(1); + Validate(basic, "value is not"); + basic.set_check_value_uint32(2); + } + + // value: uint64 + { + basic.set_check_value_uint64(5'000'000'000); + Validate(basic, "value is not"); + basic.set_check_value_uint64(5'000'000'001); + } + + // value: double + { + basic.set_check_value_double(0.99); + Validate(basic, "value is not"); + basic.set_check_value_double(1.01); + } + + // value: float + { + basic.set_check_value_float(-0.99); + Validate(basic, "value is not"); + basic.set_check_value_float(-1.01); + } + + // value: bool + { + basic.set_check_value_bool(false); + Validate(basic, "value is not"); + basic.set_check_value_bool(true); + } + + // value: string + { + basic.set_check_value_string("changeme"); + Validate(basic, "value is not"); + basic.set_check_value_string("changed"); + } + + // value: range [1;10] + { + basic.set_check_value_range_1(11); + Validate(basic, "value is not"); + basic.set_check_value_range_1(10); + } + + // value: range (1;10] + { + basic.set_check_value_range_2(1); + Validate(basic, "value is not"); + basic.set_check_value_range_2(2); + } + + // value: range [1;10) + { + basic.set_check_value_range_3(10); + Validate(basic, "value is not"); + basic.set_check_value_range_3(9); + } + + // value: range (1;10) + { + basic.set_check_value_range_4(1); + Validate(basic, "value is not"); + basic.set_check_value_range_4(5); + } + + UNIT_ASSERT(Validate(basic)); + } + + Y_UNIT_TEST(Map) { + MessageMap map; + + // size + { + Validate(map, "size is not >"); + map.mutable_values()->insert({"key", "value"}); + } + + // value's length + { + Validate(map, "value length is not <"); + map.mutable_values()->at("key") = "v"; + } + + // key's length + { + Validate(map, "key length is not <"); + map.mutable_values()->erase("key"); + map.mutable_values()->insert({"k", "v"}); + } + + // key + { + Validate(map, "key is not"); + map.mutable_values()->erase("k"); + map.mutable_values()->insert({"a", "v"}); + } + + // value + { + Validate(map, "value is not"); + map.mutable_values()->erase("a"); + map.mutable_values()->insert({"a", "b"}); + } + + UNIT_ASSERT(Validate(map)); + } + + Y_UNIT_TEST(Oneof) { + MessageOneof oneof; + + // required + { + Validate(oneof, "required but not set"); + oneof.set_check_required("set"); + } + + // check oneof (1) + { + oneof.set_check_le_1("12"); + Validate(oneof, "length is not <="); + + oneof.set_check_le_1("1"); + UNIT_ASSERT(Validate(oneof)); + } + + // check oneof (2) + { + oneof.set_check_le_2("123"); + Validate(oneof, "length is not <="); + + oneof.set_check_le_2("12"); + UNIT_ASSERT(Validate(oneof)); + } + } +} diff --git a/ydb/public/lib/validation/ut/CMakeLists.darwin.txt b/ydb/public/lib/validation/ut/CMakeLists.darwin.txt new file mode 100644 index 00000000000..301f0b6b441 --- /dev/null +++ b/ydb/public/lib/validation/ut/CMakeLists.darwin.txt @@ -0,0 +1,43 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_executable(ydb-public-lib-validation-ut) +target_include_directories(ydb-public-lib-validation-ut PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation +) +target_link_libraries(ydb-public-lib-validation-ut PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + cpp-testing-unittest + validation-ut-protos +) +target_sources(ydb-public-lib-validation-ut PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/ut.cpp +) +add_test( + NAME + ydb-public-lib-validation-ut + COMMAND + ydb-public-lib-validation-ut + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +target_link_flags(ydb-public-lib-validation-ut + PUBLIC + -Wl,-no_deduplicate + -Wl,-sdk_version,10.15 + -fPIC + -fPIC +) +vcs_info(ydb-public-lib-validation-ut) diff --git a/ydb/public/lib/validation/ut/CMakeLists.linux.txt b/ydb/public/lib/validation/ut/CMakeLists.linux.txt new file mode 100644 index 00000000000..1d7da7e31d2 --- /dev/null +++ b/ydb/public/lib/validation/ut/CMakeLists.linux.txt @@ -0,0 +1,48 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_executable(ydb-public-lib-validation-ut) +target_include_directories(ydb-public-lib-validation-ut PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation +) +target_link_libraries(ydb-public-lib-validation-ut PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-lfalloc + library-cpp-cpuid_check + cpp-testing-unittest_main + cpp-testing-unittest + validation-ut-protos +) +target_sources(ydb-public-lib-validation-ut PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/ut.cpp +) +add_test( + NAME + ydb-public-lib-validation-ut + COMMAND + ydb-public-lib-validation-ut + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +target_link_flags(ydb-public-lib-validation-ut + PUBLIC + -ldl + -lrt + -Wl,--no-as-needed + -fPIC + -fPIC + -lpthread + -lrt + -ldl +) +vcs_info(ydb-public-lib-validation-ut) diff --git a/ydb/public/lib/validation/ut/CMakeLists.txt b/ydb/public/lib/validation/ut/CMakeLists.txt new file mode 100644 index 00000000000..a681d385f3e --- /dev/null +++ b/ydb/public/lib/validation/ut/CMakeLists.txt @@ -0,0 +1,13 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (APPLE) + include(CMakeLists.darwin.txt) +elseif (UNIX) + include(CMakeLists.linux.txt) +endif() diff --git a/ydb/public/lib/validation/ut/protos/CMakeLists.txt b/ydb/public/lib/validation/ut/protos/CMakeLists.txt new file mode 100644 index 00000000000..8e1de0c27fb --- /dev/null +++ b/ydb/public/lib/validation/ut/protos/CMakeLists.txt @@ -0,0 +1,23 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(validation-ut-protos) +target_link_libraries(validation-ut-protos PUBLIC + contrib-libs-cxxsupp + yutil + api-protos-annotations + contrib-libs-protobuf +) +target_proto_messages(validation-ut-protos PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/public/lib/validation/ut/protos/validation_test.proto +) +target_proto_plugin(validation-ut-protos + validation + validation +) diff --git a/ydb/public/lib/validation/ut/protos/validation_test.proto b/ydb/public/lib/validation/ut/protos/validation_test.proto new file mode 100644 index 00000000000..f2c26ab3887 --- /dev/null +++ b/ydb/public/lib/validation/ut/protos/validation_test.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; +option cc_enable_arenas = true; + +import "ydb/public/api/protos/annotations/validation.proto"; + +package Ydb.ValidationTest; + +message EmptyMessage { +} + +message MessageWithoutValidators { + uint32 key = 1; + string value = 2; + repeated string payload = 3; +} + +message MessageBasic { + string check_required = 1 [(required) = true]; + + repeated string check_size_range_1_2 = 2 [(size).range = {min: 1, max: 2}]; + repeated string check_size_lt_2 = 3 [(size).lt = 2]; + repeated string check_size_le_1 = 4 [(size).le = 1]; + repeated string check_size_eq_1 = 5 [(size).eq = 1]; + repeated string check_size_ge_1 = 6 [(size).ge = 1]; + repeated string check_size_gt_1 = 7 [(size).gt = 1]; + + string check_length_range_1_3 = 8 [(length).range = {min: 1, max: 3}]; + string check_length_lt_4 = 9 [(length).lt = 4]; + string check_length_le_3 = 10 [(length).le = 3]; + string check_length_eq_3 = 11 [(length).eq = 3]; + string check_length_ge_3 = 12 [(length).ge = 3]; + string check_length_gt_3 = 13 [(length).gt = 3]; + + int32 check_value_int32 = 14 [(value) = "< -1"]; + int64 check_value_int64 = 15 [(value) = "< -5'000'000'000"]; + uint32 check_value_uint32 = 16 [(value) = "> 1"]; + uint64 check_value_uint64 = 17 [(value) = "> 5'000'000'000"]; + double check_value_double = 18 [(value) = "> 1.0"]; + float check_value_float = 19 [(value) = "< -1.0"]; + bool check_value_bool = 20 [(value) = "== true"]; + string check_value_string = 21 [(value) = '!= "changeme"']; + + int32 check_value_range_1 = 22 [(value) = "[1; 10]"]; + int32 check_value_range_2 = 23 [(value) = "(1; 10]"]; + int32 check_value_range_3 = 24 [(value) = "[1; 10)"]; + int32 check_value_range_4 = 25 [(value) = "(1; 10)"]; +} + +message MessageMap { + map values = 1 [(size).gt = 0, (map_key).length.lt = 2, (length).lt = 2, (map_key).value = '!= "k"', (value) = '!= "v"']; +} + +message MessageOneof { + uint32 key = 1; + string check_required = 2 [(required) = true]; + oneof value { + string check_le_1 = 3 [(length).le = 1]; + string check_le_2 = 4 [(length).le = 2]; + } +} -- cgit v1.3