diff options
author | jansenin <jansenin@yandex-team.com> | 2023-08-21 13:16:41 +0300 |
---|---|---|
committer | jansenin <jansenin@yandex-team.com> | 2023-08-21 15:10:44 +0300 |
commit | f3779eb60f392be3600b42680004f59265cfb8e0 (patch) | |
tree | a2ca771250afb32a96000be5c1da209f1b6cd58e | |
parent | 4cd60db82ef163d1c9e25435024f3289f12e0930 (diff) | |
download | ydb-f3779eb60f392be3600b42680004f59265cfb8e0.tar.gz |
Create yaml validation framework
fix some comments and rename GeneralData_ to GeneralDataPtr_
Finished validator builders and made it compile, wrote little test
Added CommonBuilderOps and TGeneralBuilder methods
31 files changed, 2397 insertions, 0 deletions
diff --git a/ydb/library/yaml_config/CMakeLists.darwin-x86_64.txt b/ydb/library/yaml_config/CMakeLists.darwin-x86_64.txt index 6925807f482..903b2701d3c 100644 --- a/ydb/library/yaml_config/CMakeLists.darwin-x86_64.txt +++ b/ydb/library/yaml_config/CMakeLists.darwin-x86_64.txt @@ -9,6 +9,7 @@ find_package(OpenSSL REQUIRED) add_subdirectory(public) add_subdirectory(ut) +add_subdirectory(validator) add_library(ydb-library-yaml_config) target_link_libraries(ydb-library-yaml_config PUBLIC diff --git a/ydb/library/yaml_config/CMakeLists.linux-aarch64.txt b/ydb/library/yaml_config/CMakeLists.linux-aarch64.txt index 7e23908a1a7..5302caa932f 100644 --- a/ydb/library/yaml_config/CMakeLists.linux-aarch64.txt +++ b/ydb/library/yaml_config/CMakeLists.linux-aarch64.txt @@ -9,6 +9,7 @@ find_package(OpenSSL REQUIRED) add_subdirectory(public) add_subdirectory(ut) +add_subdirectory(validator) add_library(ydb-library-yaml_config) target_link_libraries(ydb-library-yaml_config PUBLIC diff --git a/ydb/library/yaml_config/CMakeLists.linux-x86_64.txt b/ydb/library/yaml_config/CMakeLists.linux-x86_64.txt index 7e23908a1a7..5302caa932f 100644 --- a/ydb/library/yaml_config/CMakeLists.linux-x86_64.txt +++ b/ydb/library/yaml_config/CMakeLists.linux-x86_64.txt @@ -9,6 +9,7 @@ find_package(OpenSSL REQUIRED) add_subdirectory(public) add_subdirectory(ut) +add_subdirectory(validator) add_library(ydb-library-yaml_config) target_link_libraries(ydb-library-yaml_config PUBLIC diff --git a/ydb/library/yaml_config/CMakeLists.windows-x86_64.txt b/ydb/library/yaml_config/CMakeLists.windows-x86_64.txt index 6925807f482..903b2701d3c 100644 --- a/ydb/library/yaml_config/CMakeLists.windows-x86_64.txt +++ b/ydb/library/yaml_config/CMakeLists.windows-x86_64.txt @@ -9,6 +9,7 @@ find_package(OpenSSL REQUIRED) add_subdirectory(public) add_subdirectory(ut) +add_subdirectory(validator) add_library(ydb-library-yaml_config) target_link_libraries(ydb-library-yaml_config PUBLIC diff --git a/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt b/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt new file mode 100644 index 00000000000..d5bcdf7d865 --- /dev/null +++ b/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,32 @@ + +# This file was generated 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_subdirectory(ut) +get_built_tool_path( + TOOL_enum_parser_bin + TOOL_enum_parser_dependency + tools/enum_parser/enum_parser + enum_parser +) + +add_library(library-yaml_config-validator) +target_link_libraries(library-yaml_config-validator PUBLIC + contrib-libs-cxxsupp + yutil + cpp-yaml-fyamlcpp + tools-enum_parser-enum_serialization_runtime +) +target_sources(library-yaml_config-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp +) +generate_enum_serilization(library-yaml_config-validator + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h + INCLUDE_HEADERS + ydb/library/yaml_config/validator/validator_builder.h +) diff --git a/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt b/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt new file mode 100644 index 00000000000..5f374afe418 --- /dev/null +++ b/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt @@ -0,0 +1,33 @@ + +# This file was generated 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_subdirectory(ut) +get_built_tool_path( + TOOL_enum_parser_bin + TOOL_enum_parser_dependency + tools/enum_parser/enum_parser + enum_parser +) + +add_library(library-yaml_config-validator) +target_link_libraries(library-yaml_config-validator PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + cpp-yaml-fyamlcpp + tools-enum_parser-enum_serialization_runtime +) +target_sources(library-yaml_config-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp +) +generate_enum_serilization(library-yaml_config-validator + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h + INCLUDE_HEADERS + ydb/library/yaml_config/validator/validator_builder.h +) diff --git a/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt b/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt new file mode 100644 index 00000000000..5f374afe418 --- /dev/null +++ b/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt @@ -0,0 +1,33 @@ + +# This file was generated 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_subdirectory(ut) +get_built_tool_path( + TOOL_enum_parser_bin + TOOL_enum_parser_dependency + tools/enum_parser/enum_parser + enum_parser +) + +add_library(library-yaml_config-validator) +target_link_libraries(library-yaml_config-validator PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + cpp-yaml-fyamlcpp + tools-enum_parser-enum_serialization_runtime +) +target_sources(library-yaml_config-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp +) +generate_enum_serilization(library-yaml_config-validator + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h + INCLUDE_HEADERS + ydb/library/yaml_config/validator/validator_builder.h +) diff --git a/ydb/library/yaml_config/validator/CMakeLists.txt b/ydb/library/yaml_config/validator/CMakeLists.txt new file mode 100644 index 00000000000..f8b31df0c11 --- /dev/null +++ b/ydb/library/yaml_config/validator/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated 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 (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt b/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt new file mode 100644 index 00000000000..d5bcdf7d865 --- /dev/null +++ b/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt @@ -0,0 +1,32 @@ + +# This file was generated 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_subdirectory(ut) +get_built_tool_path( + TOOL_enum_parser_bin + TOOL_enum_parser_dependency + tools/enum_parser/enum_parser + enum_parser +) + +add_library(library-yaml_config-validator) +target_link_libraries(library-yaml_config-validator PUBLIC + contrib-libs-cxxsupp + yutil + cpp-yaml-fyamlcpp + tools-enum_parser-enum_serialization_runtime +) +target_sources(library-yaml_config-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp +) +generate_enum_serilization(library-yaml_config-validator + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h + INCLUDE_HEADERS + ydb/library/yaml_config/validator/validator_builder.h +) diff --git a/ydb/library/yaml_config/validator/ut/CMakeLists.txt b/ydb/library/yaml_config/validator/ut/CMakeLists.txt new file mode 100644 index 00000000000..67a3b0888d2 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/CMakeLists.txt @@ -0,0 +1,10 @@ + +# This file was generated 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_subdirectory(validator) +add_subdirectory(validator_builder) diff --git a/ydb/library/yaml_config/validator/ut/validator/CMakeLists.darwin-x86_64.txt b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.darwin-x86_64.txt new file mode 100644 index 00000000000..ed0bb812349 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,62 @@ + +# This file was generated 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-library-yaml_config-validator-ut-validator) +target_link_libraries(ydb-library-yaml_config-validator-ut-validator PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + library-yaml_config-validator +) +target_link_options(ydb-library-yaml_config-validator-ut-validator PRIVATE + -Wl,-platform_version,macos,11.0,11.0 + -fPIC + -fPIC +) +target_sources(ydb-library-yaml_config-validator-ut-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp +) +set_property( + TARGET + ydb-library-yaml_config-validator-ut-validator + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + ydb-library-yaml_config-validator-ut-validator + TEST_TARGET + ydb-library-yaml_config-validator-ut-validator + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + PROCESSORS + 1 +) +target_allocator(ydb-library-yaml_config-validator-ut-validator + system_allocator +) +vcs_info(ydb-library-yaml_config-validator-ut-validator) diff --git a/ydb/library/yaml_config/validator/ut/validator/CMakeLists.linux-aarch64.txt b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.linux-aarch64.txt new file mode 100644 index 00000000000..fb8cd834369 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.linux-aarch64.txt @@ -0,0 +1,67 @@ + +# This file was generated 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-library-yaml_config-validator-ut-validator) +target_link_libraries(ydb-library-yaml_config-validator-ut-validator PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + cpp-testing-unittest_main + library-yaml_config-validator +) +target_link_options(ydb-library-yaml_config-validator-ut-validator PRIVATE + -ldl + -lrt + -Wl,--no-as-needed + -fPIC + -fPIC + -lpthread + -lrt + -ldl +) +target_sources(ydb-library-yaml_config-validator-ut-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp +) +set_property( + TARGET + ydb-library-yaml_config-validator-ut-validator + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + ydb-library-yaml_config-validator-ut-validator + TEST_TARGET + ydb-library-yaml_config-validator-ut-validator + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + PROCESSORS + 1 +) +target_allocator(ydb-library-yaml_config-validator-ut-validator + cpp-malloc-jemalloc +) +vcs_info(ydb-library-yaml_config-validator-ut-validator) diff --git a/ydb/library/yaml_config/validator/ut/validator/CMakeLists.linux-x86_64.txt b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.linux-x86_64.txt new file mode 100644 index 00000000000..6017dd9f835 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.linux-x86_64.txt @@ -0,0 +1,69 @@ + +# This file was generated 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-library-yaml_config-validator-ut-validator) +target_link_libraries(ydb-library-yaml_config-validator-ut-validator PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + library-yaml_config-validator +) +target_link_options(ydb-library-yaml_config-validator-ut-validator PRIVATE + -ldl + -lrt + -Wl,--no-as-needed + -fPIC + -fPIC + -lpthread + -lrt + -ldl +) +target_sources(ydb-library-yaml_config-validator-ut-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp +) +set_property( + TARGET + ydb-library-yaml_config-validator-ut-validator + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + ydb-library-yaml_config-validator-ut-validator + TEST_TARGET + ydb-library-yaml_config-validator-ut-validator + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + PROCESSORS + 1 +) +target_allocator(ydb-library-yaml_config-validator-ut-validator + cpp-malloc-tcmalloc + libs-tcmalloc-no_percpu_cache +) +vcs_info(ydb-library-yaml_config-validator-ut-validator) diff --git a/ydb/library/yaml_config/validator/ut/validator/CMakeLists.txt b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.txt new file mode 100644 index 00000000000..f8b31df0c11 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated 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 (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/ydb/library/yaml_config/validator/ut/validator/CMakeLists.windows-x86_64.txt b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.windows-x86_64.txt new file mode 100644 index 00000000000..65ef01ab15f --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/CMakeLists.windows-x86_64.txt @@ -0,0 +1,57 @@ + +# This file was generated 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-library-yaml_config-validator-ut-validator) +target_link_libraries(ydb-library-yaml_config-validator-ut-validator PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + library-yaml_config-validator +) +target_sources(ydb-library-yaml_config-validator-ut-validator PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp +) +set_property( + TARGET + ydb-library-yaml_config-validator-ut-validator + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + ydb-library-yaml_config-validator-ut-validator + TEST_TARGET + ydb-library-yaml_config-validator-ut-validator + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + ydb-library-yaml_config-validator-ut-validator + PROPERTY + PROCESSORS + 1 +) +target_allocator(ydb-library-yaml_config-validator-ut-validator + system_allocator +) +vcs_info(ydb-library-yaml_config-validator-ut-validator) diff --git a/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp b/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp new file mode 100644 index 00000000000..1ee6a500976 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/validator_ut.cpp @@ -0,0 +1,295 @@ +#include <util/stream/str.h> + +#include <library/cpp/testing/unittest/registar.h> + +#include <ydb/library/yaml_config/validator/validator_builder.h> + +using namespace NYamlConfig::NValidator; +using TIssue = TValidationResult::TIssue; + +bool HasIssue(const TValidationResult& result, const TIssue& issue) { + for (const TIssue& i : result.Issues) { + if (i == issue) { + return true; + } + } + Cerr << "Doesn't have specified issue. The followin issues are presented:" << Endl; + Cerr << result; + Cerr << "------------- End of issue List" << Endl; + return false; +} + +bool HasOneIssue(const TValidationResult& result, const TIssue& issue) { + if (result.Issues.size() != 1) { + Cerr << "Has " << result.Issues.size() << " errors" << Endl; + return false; + } + return HasIssue(result, issue); +} + +bool HasOnlyThisIssues(TValidationResult result, TVector<TIssue> issues) { + if (result.Issues.size() != issues.size()) { + return false; + } + Sort(result.Issues); + Sort(issues); + for (size_t i = 0; i < issues.size(); ++i) { + if (result.Issues[i] != issues[i]) { + Cerr << "Issues are differend. List of actul issues:" << Endl; + Cerr << result; + Cerr << "------------- List of Expected Issues: " << Endl; + Cerr << TValidationResult(issues); + Cerr << "------------- End of issue List" << Endl; + return false; + } + } + return true; +} + +bool ContainsProblem(const TValidationResult& result, const TString& problem) { + for (const TIssue& i : result.Issues) { + if (i.Problem.Contains(problem)) { + return true; + } + } + Cerr << "Problem '" << problem << "'wasn't found" << Endl; + Cerr << "List of issues:" << Endl; + Cerr << result; + return false; +} + +Y_UNIT_TEST_SUITE(Validator) { + Y_UNIT_TEST(IntValidation) { + TInt64Builder b; + b.Range(0, 10); + auto v = b.CreateValidator(); + + Y_ENSURE(HasOneIssue(v.Validate("{}"), {"/", "Node must be Scalar(Int64)"})); + Y_ENSURE(HasOneIssue(v.Validate("1a"), {"/", "Can't convert value to Int64"})); + Y_ENSURE(HasOneIssue(v.Validate("11"), {"/", "Value must be less or equal to max value(i.e <= 10)"})); + Y_ENSURE(HasOneIssue(v.Validate("-1"), {"/", "Value must be greater or equal to min value(i.e >= 0)"})); + Y_ENSURE(v.Validate("5").Ok()); + } + + Y_UNIT_TEST(BoolValidation) { + TBoolBuilder b; + auto v = b.CreateValidator(); + + Y_ENSURE(HasOneIssue(v.Validate("{}"), {"/", "Node must be Scalar(Bool)"})); + Y_ENSURE(HasOneIssue(v.Validate("a"), {"/", "Value must be either true or false"})); + Y_ENSURE(HasOneIssue(v.Validate("1"), {"/", "Value must be either true or false"})); + Y_ENSURE(HasOneIssue(v.Validate("yes"), {"/", "Value must be either true or false"})); + + Y_ENSURE(v.Validate("true").Ok()); + Y_ENSURE(v.Validate("False").Ok()); + Y_ENSURE(v.Validate("TrUe").Ok()); + } + + Y_UNIT_TEST(StringValidation) { + NYamlConfig::NValidator::TStringBuilder b; + auto v = b.CreateValidator(); + + Y_ENSURE(HasOneIssue(v.Validate("{}"), {"/", "Node must be Scalar(String)"})); + + Y_ENSURE(v.Validate("text").Ok()); + Y_ENSURE(v.Validate("123").Ok()); + } + + Y_UNIT_TEST(IntArrayValidation) { + TArrayBuilder b; + b.Item(TInt64Builder().Range(0, 10)); + // or + // b.Int64Item([](auto& b) { b.Range(0, 10); }); + auto v = b.CreateValidator(); + + Y_ENSURE(HasOneIssue(v.Validate("{}"), {"/", "Node must be Array"})); + Y_ENSURE(HasOneIssue(v.Validate("[{}]"), {"/0", "Node must be Scalar(Int64)"})); + Y_ENSURE(HasOneIssue(v.Validate("[1a]"), {"/0", "Can't convert value to Int64"})); + Y_ENSURE(HasOneIssue(v.Validate("[11]"), {"/0", "Value must be less or equal to max value(i.e <= 10)"})); + Y_ENSURE(HasOneIssue(v.Validate("[-1]"), {"/0", "Value must be greater or equal to min value(i.e >= 0)"})); + + Y_ENSURE(HasOnlyThisIssues(v.Validate("[{}, 1a, 11, -1, 5]"), { + {"/0", "Node must be Scalar(Int64)"}, + {"/1", "Can't convert value to Int64"}, + {"/2", "Value must be less or equal to max value(i.e <= 10)"}, + {"/3", "Value must be greater or equal to min value(i.e >= 0)"}, + })); + + Y_ENSURE(v.Validate("[1, 2, 3, 3, 3]").Ok()); + Y_ENSURE(v.Validate("[0, 10, 5]").Ok()); + } + + // Also can add nullable field support + Y_UNIT_TEST(MapValidation) { + auto v = + TMapBuilder() + .Int64("required_int") + .Int64("int", [](auto& b) { + b.Optional() + .Range(0, 10); + }) + .Bool("bool") + .Array("int_array", [](auto& b) { + b.Int64Item([](auto& b){ b.Range(0, 10); }); + }) + .CreateValidator(); + + Y_ENSURE(HasOneIssue(v.Validate("213"), {"/", "Node must be Map"})); + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "int: 5,\n" + "bool: true,\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(v.Validate(yaml).Ok()); + } + + { + TString yaml = + "{\n" + "int: 5,\n" + "bool: true,\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(HasOneIssue(v.Validate(yaml), {"/required_int", "Node is required"})); + } + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "bool: true,\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(v.Validate(yaml).Ok()); + } + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "int: -1,\n" + "bool: true,\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(HasOneIssue(v.Validate(yaml), {"/int", "Value must be greater or equal to min value(i.e >= 0)"})); + } + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "int: 11,\n" + "bool: true,\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(HasOneIssue(v.Validate(yaml), {"/int", "Value must be less or equal to max value(i.e <= 10)"})); + } + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "int: 5,\n" + "bool: {},\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(HasOneIssue(v.Validate(yaml), {"/bool", "Node must be Scalar(Bool)"})); + } + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "int: 5,\n" + "bool: not_bool,\n" + "int_array: [1,2,3,4,5]\n" + "}"; + Y_ENSURE(HasOneIssue(v.Validate(yaml), {"/bool", "Value must be either true or false"})); + } + + { + TString yaml = + "{\n" + "required_int: 5,\n" + "int: 5,\n" + "bool: true,\n" + "int_array: [{}, 1a, 11, -1, 5]\n" + "}"; + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/int_array/0", "Node must be Scalar(Int64)"}, + {"/int_array/1", "Can't convert value to Int64"}, + {"/int_array/2", "Value must be less or equal to max value(i.e <= 10)"}, + {"/int_array/3", "Value must be greater or equal to min value(i.e >= 0)"}, + })); + } + + { + TString yaml = + "{\n" + "int: -1,\n" + "bool: not_bool,\n" + "int_array: [{}, 1a, 11, -1, 5]\n" + "}"; + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/required_int", "Node is required"}, + {"/int", "Value must be greater or equal to min value(i.e >= 0)"}, + {"/bool", "Value must be either true or false"}, + {"/int_array/0", "Node must be Scalar(Int64)"}, + {"/int_array/1", "Can't convert value to Int64"}, + {"/int_array/2", "Value must be less or equal to max value(i.e <= 10)"}, + {"/int_array/3", "Value must be greater or equal to min value(i.e >= 0)"}, + })); + } + } + + Y_UNIT_TEST(MultitypeNodeValidation) { + TGenericBuilder b; + b.CanBeBool(); + b.CanBeInt64([](auto& b) { b.Range(0, 10); }); + b.CanBeMap([](auto& b){ + b.Int64("int1") + .Int64("int2"); + }); + + auto v = b.CreateValidator(); + + Y_ENSURE(v.Validate("true").Ok()); + Y_ENSURE(v.Validate("FaLsE").Ok()); + Y_ENSURE(v.Validate("0").Ok()); + Y_ENSURE(v.Validate("10").Ok()); + Y_ENSURE(v.Validate("{int1: 1, int2: 2}").Ok()); + + // should change error mesage somehow + Y_ENSURE(HasOneIssue(v.Validate("akaka"), { + "/", "There was errors in all scenarios:\n" + " 1) Value must be either true or false\n" + " 2) Can't convert value to Int64\n" + " 3) Node must be Map" + })); + } + + Y_UNIT_TEST(OpaqueMaps) { + auto b = + TMapBuilder() + .Int64("int"); + + auto notOpaque = b.CreateValidator(); + + b.Opaque(); + auto opaque = b.CreateValidator(); + + Y_ENSURE(notOpaque.Validate("{int: 1}").Ok()); + Y_ENSURE(HasOneIssue(notOpaque.Validate("{}"), {"/int", "Node is required"})); + Y_ENSURE(HasOneIssue( + notOpaque.Validate("{int: 1, int2: 2}"), + {"/int2", "Unexpected node"})); + + Y_ENSURE(opaque.Validate("{int: 1}").Ok()); + Y_ENSURE(HasOneIssue(opaque.Validate("{}"), {"/int", "Node is required"})); + Y_ENSURE(opaque.Validate("{int: 1, int2: 2}").Ok()); + } +} diff --git a/ydb/library/yaml_config/validator/ut/validator/ya.make b/ydb/library/yaml_config/validator/ut/validator/ya.make new file mode 100644 index 00000000000..d01c6cf0e77 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator/ya.make @@ -0,0 +1,11 @@ +UNITTEST() + +SRCS( + validator_ut.cpp +) + +PEERDIR( + ydb/library/yaml_config/validator +) + +END() diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.darwin-x86_64.txt b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.darwin-x86_64.txt new file mode 100644 index 00000000000..7584c9fe36e --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,62 @@ + +# This file was generated 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(yaml_config-validator-ut-validator_builder) +target_link_libraries(yaml_config-validator-ut-validator_builder PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + library-yaml_config-validator +) +target_link_options(yaml_config-validator-ut-validator_builder PRIVATE + -Wl,-platform_version,macos,11.0,11.0 + -fPIC + -fPIC +) +target_sources(yaml_config-validator-ut-validator_builder PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp +) +set_property( + TARGET + yaml_config-validator-ut-validator_builder + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + yaml_config-validator-ut-validator_builder + TEST_TARGET + yaml_config-validator-ut-validator_builder + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + PROCESSORS + 1 +) +target_allocator(yaml_config-validator-ut-validator_builder + system_allocator +) +vcs_info(yaml_config-validator-ut-validator_builder) diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.linux-aarch64.txt b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.linux-aarch64.txt new file mode 100644 index 00000000000..cb1b241848e --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.linux-aarch64.txt @@ -0,0 +1,67 @@ + +# This file was generated 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(yaml_config-validator-ut-validator_builder) +target_link_libraries(yaml_config-validator-ut-validator_builder PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + cpp-testing-unittest_main + library-yaml_config-validator +) +target_link_options(yaml_config-validator-ut-validator_builder PRIVATE + -ldl + -lrt + -Wl,--no-as-needed + -fPIC + -fPIC + -lpthread + -lrt + -ldl +) +target_sources(yaml_config-validator-ut-validator_builder PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp +) +set_property( + TARGET + yaml_config-validator-ut-validator_builder + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + yaml_config-validator-ut-validator_builder + TEST_TARGET + yaml_config-validator-ut-validator_builder + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + PROCESSORS + 1 +) +target_allocator(yaml_config-validator-ut-validator_builder + cpp-malloc-jemalloc +) +vcs_info(yaml_config-validator-ut-validator_builder) diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.linux-x86_64.txt b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.linux-x86_64.txt new file mode 100644 index 00000000000..a3875ea50b6 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.linux-x86_64.txt @@ -0,0 +1,69 @@ + +# This file was generated 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(yaml_config-validator-ut-validator_builder) +target_link_libraries(yaml_config-validator-ut-validator_builder PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + library-yaml_config-validator +) +target_link_options(yaml_config-validator-ut-validator_builder PRIVATE + -ldl + -lrt + -Wl,--no-as-needed + -fPIC + -fPIC + -lpthread + -lrt + -ldl +) +target_sources(yaml_config-validator-ut-validator_builder PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp +) +set_property( + TARGET + yaml_config-validator-ut-validator_builder + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + yaml_config-validator-ut-validator_builder + TEST_TARGET + yaml_config-validator-ut-validator_builder + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + PROCESSORS + 1 +) +target_allocator(yaml_config-validator-ut-validator_builder + cpp-malloc-tcmalloc + libs-tcmalloc-no_percpu_cache +) +vcs_info(yaml_config-validator-ut-validator_builder) diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.txt b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.txt new file mode 100644 index 00000000000..f8b31df0c11 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated 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 (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.windows-x86_64.txt b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.windows-x86_64.txt new file mode 100644 index 00000000000..e83b948e00e --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/CMakeLists.windows-x86_64.txt @@ -0,0 +1,57 @@ + +# This file was generated 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(yaml_config-validator-ut-validator_builder) +target_link_libraries(yaml_config-validator-ut-validator_builder PUBLIC + contrib-libs-cxxsupp + yutil + library-cpp-cpuid_check + cpp-testing-unittest_main + library-yaml_config-validator +) +target_sources(yaml_config-validator-ut-validator_builder PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp +) +set_property( + TARGET + yaml_config-validator-ut-validator_builder + PROPERTY + SPLIT_FACTOR + 1 +) +add_yunittest( + NAME + yaml_config-validator-ut-validator_builder + TEST_TARGET + yaml_config-validator-ut-validator_builder + TEST_ARG + --print-before-suite + --print-before-test + --fork-tests + --print-times + --show-fails +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + LABELS + SMALL +) +set_yunittest_property( + TEST + yaml_config-validator-ut-validator_builder + PROPERTY + PROCESSORS + 1 +) +target_allocator(yaml_config-validator-ut-validator_builder + system_allocator +) +vcs_info(yaml_config-validator-ut-validator_builder) diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp b/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp new file mode 100644 index 00000000000..688f491c4b9 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/validator_builder_ut.cpp @@ -0,0 +1,92 @@ +#include <util/stream/str.h> + +#include <library/cpp/testing/unittest/registar.h> + +#include <ydb/library/yaml_config/validator/validator_builder.h> + +using namespace NYamlConfig::NValidator; + +Y_UNIT_TEST_SUITE(ValidatorBuilder) { + Y_UNIT_TEST(CanCreateAllTypesOfNodes) { + TGenericBuilder v0; + TMapBuilder v1; + TArrayBuilder v2; + TInt64Builder v3; + NYamlConfig::NValidator::TStringBuilder v4; + TBoolBuilder v5; + } + + Y_UNIT_TEST(BuildSimpleValidator) { + TMapBuilder dimensions([](auto& b){ + auto greaterThanZero = [](TInt64Builder& b) { + b.Min(0); + }; + b.Int64("length", greaterThanZero); + b.Int64("width", greaterThanZero); + b.Int64("height", greaterThanZero); + }); + + auto tagsConfigurator = [](auto& b){ + b.Description("Tags for the product") + .Optional() + .Unique() + .StringItem(); + }; + + auto map = TMapBuilder() + .Description("Map desription") + .Int64("productId", [](auto& b) { + b.Description("TString description"); + }) + .String("productName", [](auto& b) { + b.Description("Name of the product"); + }) + .Int64("price", [](auto& b) { + b.Description("The price of the product") + .Min(0); + }) + .Array("tags", tagsConfigurator) + .Field("dimensions", dimensions); + + map.MapAt("dimensions").Description("product size"); + } + + Y_UNIT_TEST(CanHaveMultipleType) { + TGenericBuilder m; + m.CanBeMap(); + m.CanBeBool(); + m.CanBeInt64(); + TGenericBuilder m2; + m2.CanBeInt64(); + m2.CanBeBool(); + m.CanBe(m2); + } + + Y_UNIT_TEST(CanHaveDuplicateType) { + TGenericBuilder m; + m.CanBeMap([](auto& b){ b.Int64("int_field"); }); + m.CanBeMap([](auto& b){ b.Int64("int_field2"); }); + m.CanBeBool(); + } + + Y_UNIT_TEST(CreateMultitypeNode) { + TGenericBuilder b1; + b1.CanBeMap(); + b1.CanBeInt64(); + b1.CanBeString(); + TGenericBuilder b2; + b2.CanBeInt64(); + b2.CanBeString(); + b2.CanBeArray(); + TMapBuilder b3; + b3 + .Int64("int_f") + .String("string_f") + .Map("map"); + TGenericBuilder final_b; + final_b.CanBeInt64(); + final_b.CanBe(b1); + final_b.CanBe(b2); + final_b.CanBe(b3); + } +} diff --git a/ydb/library/yaml_config/validator/ut/validator_builder/ya.make b/ydb/library/yaml_config/validator/ut/validator_builder/ya.make new file mode 100644 index 00000000000..3e7691e385f --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_builder/ya.make @@ -0,0 +1,11 @@ +UNITTEST() + +SRCS( + validator_builder_ut.cpp +) + +PEERDIR( + ydb/library/yaml_config/validator +) + +END() diff --git a/ydb/library/yaml_config/validator/ut/ya.make b/ydb/library/yaml_config/validator/ut/ya.make new file mode 100644 index 00000000000..3a4573f2fb6 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/ya.make @@ -0,0 +1,4 @@ +RECURSE_FOR_TESTS( + validator + validator_builder +) diff --git a/ydb/library/yaml_config/validator/validator.cpp b/ydb/library/yaml_config/validator/validator.cpp new file mode 100644 index 00000000000..87f229daf3b --- /dev/null +++ b/ydb/library/yaml_config/validator/validator.cpp @@ -0,0 +1,334 @@ +#include "validator.h" + +#include <util/string/cast.h> +#include <util/system/types.h> + +#include <utility> + +using namespace NFyaml; + +namespace NYamlConfig::NValidator { + +TValidationResult::TIssue::TIssue() {} + +TValidationResult::TIssue::TIssue(const TString& NodePath, const TString& Problem) + : NodePath(NodePath) + , Problem(Problem) {} + +TValidationResult::TIssue::TIssue(TIssue&& other) + : NodePath(std::move(other.NodePath)) + , Problem(std::move(other.Problem)) {} + +TValidationResult::TIssue::TIssue(const TIssue& other) + : NodePath(other.NodePath) + , Problem(other.Problem) {} + +TValidationResult::TIssue& TValidationResult::TIssue::operator=(TIssue&& other) { + NodePath = std::move(other.NodePath); + Problem = std::move(other.Problem); + return *this; +} + +TValidationResult::TIssue& TValidationResult::TIssue::operator=(const TIssue& other) { + NodePath = other.NodePath; + Problem = other.Problem; + return *this; +} + +TValidationResult::TValidationResult() {} + +TValidationResult::TValidationResult(TVector<TValidationResult::TIssue> issues) + : Issues(std::move(issues)) {} + +TValidationResult::TValidationResult(TValidationResult&& validationResult) + : Issues(std::move(validationResult.Issues)) {} + +TValidationResult::TValidationResult(const TValidationResult& validationResult) + : Issues(validationResult.Issues) {} + +TValidationResult& TValidationResult::operator=(TValidationResult&& validationResult) { + Issues = std::move(validationResult.Issues); + return *this; +} + +TValidationResult& TValidationResult::operator=(const TValidationResult& validationResult) { + Issues = validationResult.Issues; + return *this; +} + +bool TValidationResult::Ok() { + return Issues.empty(); +} + +void TValidationResult::AddValidationErrors(TValidationResult validationResult) { + for (auto& issue : validationResult.Issues) { + Issues.emplace_back(std::move(issue)); + } +} + +void TValidationResult::AddIssue(TIssue issue) { + Issues.emplace_back(std::move(issue)); +} + +bool TValidationResult::TIssue::operator<(const TValidationResult::TIssue& other) const { + return std::forward_as_tuple(Problem, NodePath) < std::forward_as_tuple(other.Problem, other.NodePath); +} + +bool TValidationResult::TIssue::operator==(const TValidationResult::TIssue& other) const { + return std::forward_as_tuple(Problem, NodePath) == std::forward_as_tuple(other.Problem, other.NodePath); +} + +TValidationResult TValidator::Validate(const TString& documentStr) { + TDocument document = TDocument::Parse(documentStr); + return Validate(document.Root()); +} + +TValidator::TValidator() {} + +TValidator::TValidator(TValidator&& validator) + : Required_(validator.Required_) {} + +TValidator::~TValidator() {} + +TGenericValidator::TGenericValidator() {} + +TGenericValidator::TGenericValidator(TGenericValidator&& validator) + : TValidator(std::move(validator)) + , ValidatorPtrs_(std::move(validator.ValidatorPtrs_)) {} + +TValidationResult TGenericValidator::Validate(const NFyaml::TNodeRef& node) { + if (ValidatorPtrs_.size() == 1) { + return ValidatorPtrs_[0]->Validate(node); + } + + TVector<TValidationResult> validationResults; + + for (const auto& validatorPtr : ValidatorPtrs_) { + TValidationResult result = validatorPtr->Validate(node); + if (result.Ok()) { + return {}; + } else { + validationResults.push_back(result); + } + } + + TString message = "There was errors in all scenarios:\n"; + + int scenarioIndex = 1; + bool addNewLine = false; + for (const TValidationResult& vr : validationResults) { + TString validationMesage; + bool oneIssue = vr.Issues.size() == 1; + if (oneIssue) { + validationMesage = vr.Issues[0].Problem; + } else { + bool addNewline = false; + for (const TValidationResult::TIssue& issue : vr.Issues) { + // maybe fix for multiline messages + validationMesage += ToString(addNewline ? "\n" : "" ) + " - " + issue.Problem; + addNewline = true; + } + } + message += ToString(addNewLine ? "\n" : "") + " " + ToString(scenarioIndex) + ") " + (oneIssue ? "" : "\n") + validationMesage; + scenarioIndex++; + addNewLine = true; + } + + return TVector<TValidationResult::TIssue>{{ + node.Path(), + message + }}; +} + +void TGenericValidator::AddValidator(TSimpleSharedPtr<TValidator> validatorPtr) { + ValidatorPtrs_.emplace_back(std::move(validatorPtr)); +} + +TMapValidator::TMapValidator() {} + +TMapValidator::TMapValidator(TMapValidator&& validator) + : TValidator(std::move(validator)) + , Children_(std::move(validator.Children_)) + , Opaque_(validator.Opaque_) {} + +TMapValidator::TMapValidator(THashMap<TString, TSimpleSharedPtr<TValidator>>&& children, bool Opaque) + : Children_(std::move(children)) + , Opaque_(Opaque) {} + +TValidationResult TMapValidator::Validate(const TNodeRef& node) { + if (node.Type() != ENodeType::Mapping) { + return TValidationResult({{ + node.Path(), + "Node must be Map" + }}); + } + + TString nodePath = node.Path(); + if (nodePath == "/") nodePath = ""; + + TValidationResult validationResult; + for (const auto& [name, validatorPtr] : Children_) { + TValidator& validator = *validatorPtr.Get(); + + if (node.Map().Has(name)) { + validationResult.AddValidationErrors(validator.Validate(node.Map().at(name))); + } else if (validator.Required_) { + validationResult.AddIssue({ + nodePath + "/" + name, + "Node is required" + }); + } + } + + if (!Opaque_) { + THashSet<TString> allowed; + for (const auto& child : Children_) { + allowed.insert(child.first); + } + + for (const auto& mapNode : node.Map()) { + // should check for non-scalar? + TString name = mapNode.Key().Scalar(); + if (!allowed.contains(name)) { + validationResult.AddIssue({ + nodePath + "/" + name, + "Unexpected node" + }); + } + } + } + + return validationResult; +} + +TArrayValidator::TArrayValidator() {} + +TArrayValidator::TArrayValidator(TArrayValidator&& validator) + : TValidator(std::move(validator)) + , ItemValidatorPtr_(std::move(validator.ItemValidatorPtr_)) + , Unique_(validator.Unique_) {} + +TArrayValidator::TArrayValidator(TSimpleSharedPtr<TValidator> itemValidatorPtr, bool Unique) + : ItemValidatorPtr_(std::move(itemValidatorPtr)), Unique_(Unique) {} + +TValidationResult TArrayValidator::Validate(const TNodeRef& node) { + if (node.Type() != ENodeType::Sequence) { + return TValidationResult({{ + node.Path(), + "Node must be Array" + }}); + } + + TValidationResult validationResult; + for (const auto& item : node.Sequence()) { + validationResult.AddValidationErrors(ItemValidatorPtr_->Validate(item)); + } + + // TODO: check uniqueness + Y_UNUSED(Unique_); + return validationResult; +} + +TInt64Validator::TInt64Validator() {} + +TInt64Validator::TInt64Validator(TInt64Validator&& validator) + : TValidator(std::move(validator)) + , Min_(validator.Min_) + , Max_(validator.Max_) {} + +TInt64Validator::TInt64Validator(i64 min, i64 max) + : Min_(min) + , Max_(max) {} + +TValidationResult TInt64Validator::Validate(const TNodeRef& node) { + if (node.Type() != ENodeType::Scalar) { + return TValidationResult({{ + node.Path(), + "Node must be Scalar(Int64)" + }}); + } + + TValidationResult validationResult; + + if (auto res = TryFromString<i64>(node.Scalar())) { + i64 intRes = res.GetRef(); + if (Min_ > intRes) { + validationResult.AddIssue({ + node.Path(), + TString("Value must be greater or equal to min value(i.e >= ") + ToString(Min_) + ")" + }); + } + if (Max_ < intRes) { + validationResult.AddIssue({ + node.Path(), + TString("Value must be less or equal to max value(i.e <= ") + ToString(Max_) + ")" + }); + } + } else { + validationResult.AddIssue({ + node.Path(), + "Can't convert value to Int64" + }); + } + + return validationResult; +} + +void TInt64Validator::SetMin(i64 min) { + Min_ = min; +} + +void TInt64Validator::SetMax(i64 max) { + Max_ = max; +} + +TStringValidator::TStringValidator() {} + +TStringValidator::TStringValidator(TStringValidator&& validator) + : TValidator(std::move(validator)) {} + +TValidationResult TStringValidator::Validate(const TNodeRef& node) { + if (node.Type() != ENodeType::Scalar) { + return TValidationResult({{ + node.Path(), + "Node must be Scalar(String)" + }}); + } + return {}; +} + +TBoolValidator::TBoolValidator() {} + +TBoolValidator::TBoolValidator(TBoolValidator&& validator) + : TValidator(std::move(validator)) {} + +TValidationResult TBoolValidator::Validate(const TNodeRef& node) { + if (node.Type() != ENodeType::Scalar) { + return TValidationResult({{ + node.Path(), + "Node must be Scalar(Bool)" + }}); + } + + TString value = node.Scalar(); + value.to_lower(); // is this correct to_lower? + if (value != "true" && value != "false") { + return TValidationResult({{ + node.Path(), + "Value must be either true or false" + }}); + } + return {}; +} + +} // namespace NYamlConfig::NValidator + +IOutputStream& operator<<(IOutputStream& out, const NYamlConfig::NValidator::TValidationResult::TIssue& issue) { + return out << issue.NodePath << ": " << issue.Problem; +} + +Y_DECLARE_OUT_SPEC(, NYamlConfig::NValidator::TValidationResult, out, result) { +for (const auto& issue : result.Issues) { + out << issue << ";" << Endl; + } +} diff --git a/ydb/library/yaml_config/validator/validator.h b/ydb/library/yaml_config/validator/validator.h new file mode 100644 index 00000000000..a186c32d427 --- /dev/null +++ b/ydb/library/yaml_config/validator/validator.h @@ -0,0 +1,183 @@ +#pragma once + +#include <library/cpp/yaml/fyamlcpp/fyamlcpp.h> + +#include <util/generic/string.h> +#include <util/generic/vector.h> +#include <util/generic/ptr.h> +#include <util/stream/output.h> +#include <util/generic/hash.h> +#include <util/system/types.h> +#include <util/generic/ylimits.h> + +namespace NYamlConfig::NValidator { + +class TValidator; +class TGenericValidator; +class TMapValidator; +class TArrayValidator; +class TInt64Validator; +class TStringValidator; +class TBoolValidator; + +class TGenericBuilder; + +namespace NDetail { + +class TBuilder; + +TSimpleSharedPtr<TValidator> CreateValidatorPtr(const TSimpleSharedPtr<NDetail::TBuilder>& builder); + +} + +class TValidationResult { + friend class TGenericValidator; + friend class TMapValidator; + friend class TArrayValidator; + friend class TInt64Validator; + friend class TStringValidator; + friend class TBoolValidator; + +public: + struct TIssue { + TString NodePath; + TString Problem; + + TIssue(); + TIssue(const TString& NodePath, const TString& Problem); + TIssue(const TIssue& other); + TIssue(TIssue&& other); + TIssue& operator=(TIssue&& other); + TIssue& operator=(const TIssue& other); + + bool operator<(const TValidationResult::TIssue& other) const; + bool operator==(const TValidationResult::TIssue& other) const; + }; + + TValidationResult(); + TValidationResult(TVector<TIssue> issues); + + TValidationResult(const TValidationResult& validationResult); + TValidationResult(TValidationResult&& validationResult); + + TValidationResult& operator=(TValidationResult&& validationResult); + TValidationResult& operator=(const TValidationResult& validationResult); + + bool Ok(); + + TVector<TIssue> Issues; + +private: + void AddValidationErrors(TValidationResult validationResult); + void AddIssue(TIssue issue); +}; + +class TValidator { + friend TSimpleSharedPtr<TValidator> NYamlConfig::NValidator::NDetail::CreateValidatorPtr(const TSimpleSharedPtr<NDetail::TBuilder>& builder); + friend class TMapValidator; // for .Required_ + friend class TGenericBuilder; // for .Required_ + +public: + TValidator(); + + virtual TValidationResult Validate(const NFyaml::TNodeRef& node) = 0; + TValidationResult Validate(const TString& document); + + virtual ~TValidator(); + +protected: + TValidator(TValidator&& validator); + +private: + bool Required_ = true; +}; + +class TGenericValidator : public TValidator { +public: + TGenericValidator(); + + TGenericValidator(TGenericValidator&& validator); + + TValidationResult Validate(const NFyaml::TNodeRef& node) override; + using TValidator::Validate; + + void AddValidator(TSimpleSharedPtr<TValidator> validatorPtr); + +private: + TVector<TSimpleSharedPtr<TValidator>> ValidatorPtrs_; +}; + +class TMapValidator : public TValidator { +public: + TMapValidator(); + + TMapValidator(TMapValidator&& validator); + + explicit TMapValidator(THashMap<TString, TSimpleSharedPtr<TValidator>>&& children, bool Opaque); + + TValidationResult Validate(const NFyaml::TNodeRef& node) override; + using TValidator::Validate; + +private: + THashMap<TString, TSimpleSharedPtr<TValidator>> Children_; + bool Opaque_; +}; + +class TArrayValidator : public TValidator { +public: + TArrayValidator(); + + TArrayValidator(TArrayValidator&& validator); + + TArrayValidator(TSimpleSharedPtr<TValidator> itemValidatorPtr, bool Unique); + + TValidationResult Validate(const NFyaml::TNodeRef& node) override; + using TValidator::Validate; + +private: + TSimpleSharedPtr<TValidator> ItemValidatorPtr_; + bool Unique_; +}; + +class TInt64Validator : public TValidator { +public: + TInt64Validator(); + + TInt64Validator(TInt64Validator&& validator); + + TInt64Validator(i64 min, i64 max); + + TValidationResult Validate(const NFyaml::TNodeRef& node) override; + using TValidator::Validate; + +private: + i64 Min_ = Min<i64>(); + i64 Max_ = Max<i64>(); + + void SetMin(i64 min); + void SetMax(i64 max); +}; + +class TStringValidator : public TValidator { +public: + TStringValidator(); + + TStringValidator(TStringValidator&& validator); + + TValidationResult Validate(const NFyaml::TNodeRef& node) override; + using TValidator::Validate; +}; + +class TBoolValidator : public TValidator { +public: + TBoolValidator(); + + TBoolValidator(TBoolValidator&& validator); + + TValidationResult Validate(const NFyaml::TNodeRef& node) override; + using TValidator::Validate; +}; + +} // namespace NYamlConfig::NValidator + +IOutputStream& operator<<(IOutputStream& out, const NYamlConfig::NValidator::TValidationResult::TIssue& issue); diff --git a/ydb/library/yaml_config/validator/validator_builder.cpp b/ydb/library/yaml_config/validator/validator_builder.cpp new file mode 100644 index 00000000000..df62cc82dd4 --- /dev/null +++ b/ydb/library/yaml_config/validator/validator_builder.cpp @@ -0,0 +1,449 @@ +#include "validator_builder.h" + +#include "validator.h" + +#include <util/system/types.h> + +#include <utility> + +namespace NYamlConfig::NValidator { + +namespace NDetail { + +TSimpleSharedPtr<TValidator> CreateValidatorPtr(const TSimpleSharedPtr<TBuilder>& builder) { + switch (builder->BuilderType_) { + case EBuilderType::Generic: { + TGenericValidator v = static_cast<TGenericBuilder*>(builder.Get())->CreateValidator(); + v.Required_ = builder->Required_; + return TSimpleSharedPtr<TValidator>(new TGenericValidator(std::move(v))); + } + case EBuilderType::Map: { + TMapValidator v = static_cast<TMapBuilder*>(builder.Get())->CreateValidator(); + v.Required_ = builder->Required_; + return TSimpleSharedPtr<TValidator>(new TMapValidator(std::move(v))); + } + case EBuilderType::Array: { + TArrayValidator v = static_cast<TArrayBuilder*>(builder.Get())->CreateValidator(); + v.Required_ = builder->Required_; + return TSimpleSharedPtr<TValidator>(new TArrayValidator(std::move(v))); + } + case EBuilderType::Int64: { + TInt64Validator v = static_cast<TInt64Builder*>(builder.Get())->CreateValidator(); + v.Required_ = builder->Required_; + return TSimpleSharedPtr<TValidator>(new TInt64Validator(std::move(v))); + } + case EBuilderType::String: { + TStringValidator v = static_cast<TStringBuilder*>(builder.Get())->CreateValidator(); + v.Required_ = builder->Required_; + return TSimpleSharedPtr<TValidator>(new TStringValidator(std::move(v))); + } + case EBuilderType::Bool: { + TBoolValidator v = static_cast<TBoolBuilder*>(builder.Get())->CreateValidator(); + v.Required_ = builder->Required_; + return TSimpleSharedPtr<TValidator>(new TBoolValidator(std::move(v))); + } + } +} + +TBuilder::TBuilder(EBuilderType builderType) + : BuilderType_(builderType) {} + +TBuilder& TBuilder::operator=(const TBuilder& builder) { + Y_ASSERT(BuilderType_ == builder.BuilderType_); + Required_ = builder.Required_; + Description_ = builder.Description_; + return *this; +} + +TBuilder& TBuilder::operator=(TBuilder&& builder) { + Y_ASSERT(BuilderType_ == builder.BuilderType_); + Required_ = builder.Required_; + Description_ = std::move(builder.Description_); + return *this; +} + +} // namespace NDetail + +TGenericBuilder::NodeTypeAndBuilder::NodeTypeAndBuilder() {} + +TGenericBuilder::NodeTypeAndBuilder::NodeTypeAndBuilder(EBuilderType type, TSimpleSharedPtr<TBuilder> builder) + : Type(type) + , Builder(std::move(builder)) {} + + +// TGenericBuilder +TGenericBuilder::TGenericBuilder() + : TCommonBuilderOps<TGenericBuilder>(EBuilderType::Generic) {}; + +TGenericBuilder::TGenericBuilder(const TGenericBuilder& builder) + : TCommonBuilderOps<TGenericBuilder>(builder) + , PossibleBuilderPtrs_(builder.PossibleBuilderPtrs_) {} + +TGenericBuilder::TGenericBuilder(TGenericBuilder&& builder) + : TCommonBuilderOps<TGenericBuilder>(std::move(builder)) + , PossibleBuilderPtrs_(std::move(builder.PossibleBuilderPtrs_)) {} + +TGenericBuilder& TGenericBuilder::operator=(const TGenericBuilder& builder) { + TBuilder::operator=(builder); + PossibleBuilderPtrs_ = builder.PossibleBuilderPtrs_; + return *this; +} + +TGenericBuilder& TGenericBuilder::operator=(TGenericBuilder&& builder) { + TBuilder::operator=(std::move(builder)); + PossibleBuilderPtrs_ = std::move(builder.PossibleBuilderPtrs_); + return *this; +} + +TGenericBuilder::TGenericBuilder(std::function<void(TGenericBuilder&)> configurator) + :TGenericBuilder() { + configurator(*this); +} + +TGenericBuilder& TGenericBuilder::CanBeMap(std::function<void(TMapBuilder&)> configurator) { + PossibleBuilderPtrs_.emplace_back(new TMapBuilder(configurator)); + return *this; +} + +TGenericBuilder& TGenericBuilder::CanBeArray(std::function<void(TArrayBuilder&)> configurator) { + PossibleBuilderPtrs_.emplace_back(new TArrayBuilder(configurator)); + return *this; +} + +TGenericBuilder& TGenericBuilder::CanBeInt64(std::function<void(TInt64Builder&)> configurator) { + PossibleBuilderPtrs_.emplace_back(new TInt64Builder(configurator)); + return *this; +} + +TGenericBuilder& TGenericBuilder::CanBeString(std::function<void(TStringBuilder&)> configurator) { + PossibleBuilderPtrs_.emplace_back(new TStringBuilder(configurator)); + return *this; +} + +TGenericBuilder& TGenericBuilder::CanBeBool(std::function<void(TBoolBuilder&)> configurator) { + PossibleBuilderPtrs_.emplace_back(new TBoolBuilder(configurator)); + return *this; +} + +TGenericValidator TGenericBuilder::CreateValidator() { + TGenericValidator result; + for (const auto& builderPtr : PossibleBuilderPtrs_) { + result.AddValidator(NDetail::CreateValidatorPtr(builderPtr)); + } + + result.Required_ = Required_; + + return result; +} + +TMapBuilder::TMapBuilder() + : TCommonBuilderOps<TMapBuilder>(EBuilderType::Map) {} + +TMapBuilder::TMapBuilder(const TMapBuilder& builder) + : TCommonBuilderOps<TMapBuilder>(builder) + , Children_(builder.Children_) + , Opaque_(builder.Opaque_) {} + +TMapBuilder::TMapBuilder(TMapBuilder&& builder) + : TCommonBuilderOps<TMapBuilder>(std::move(builder)) + , Children_(std::move(builder.Children_)) + , Opaque_(builder.Opaque_) {} + +TMapBuilder& TMapBuilder::operator=(const TMapBuilder& builder) { + TBuilder::operator=(builder); + Children_ = builder.Children_; + Opaque_ = builder.Opaque_; + return *this; +} + +TMapBuilder& TMapBuilder::operator=(TMapBuilder&& builder) { + TBuilder::operator=(std::move(builder)); + Children_ = std::move(builder.Children_); + Opaque_ = builder.Opaque_; + return *this; +} + +TMapBuilder::TMapBuilder(std::function<void(TMapBuilder&)> configurator) + :TMapBuilder() { + configurator(*this); +} + +void TMapBuilder::ThrowIfAlreadyHasField(const TString& field) { + if (Children_.contains(field)) { + ythrow BuilderException() << "Node already has field \"" << field << "\""; + } +} + +TMapBuilder& TMapBuilder::GenericField(const TString& field, std::function<void(TGenericBuilder&)> configurator) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new TGenericBuilder(configurator)); + return *this; +} + +TMapBuilder& TMapBuilder::Map(const TString& field, std::function<void(TMapBuilder&)> configurator) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new TMapBuilder(configurator)); + return *this; +} + +TMapBuilder& TMapBuilder::Array(const TString& field, std::function<void(TArrayBuilder&)> configurator) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new TArrayBuilder(configurator)); + return *this; +} + +TMapBuilder& TMapBuilder::Int64(const TString& field, std::function<void(TInt64Builder&)> configurator) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new TInt64Builder(configurator)); + return *this; +} + +TMapBuilder& TMapBuilder::String(const TString& field, std::function<void(TStringBuilder&)> configurator) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new TStringBuilder(configurator)); + return *this; +} + +TMapBuilder& TMapBuilder::Bool(const TString& field, std::function<void(TBoolBuilder&)> configurator) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new TBoolBuilder(configurator)); + return *this; +} + +TMapBuilder& TMapBuilder::Opaque() { + Opaque_ = true; + return *this; +} + +TMapBuilder& TMapBuilder::NotOpaque() { + Opaque_ = false; + return *this; +} + +bool TMapBuilder::HasField(const TString& field) { + return Children_.contains(field); +} + +TGenericBuilder& TMapBuilder::GenericFieldAt(const TString& field) { + return static_cast<TGenericBuilder&>(*Children_.at(field).Get()); +} + +TMapBuilder& TMapBuilder::MapAt(const TString& field) { + return static_cast<TMapBuilder&>(*Children_.at(field).Get()); +} + +TArrayBuilder& TMapBuilder::ArrayAt(const TString& field) { + return static_cast<TArrayBuilder&>(*Children_.at(field).Get()); +} + +TInt64Builder& TMapBuilder::Int64At(const TString& field) { + return static_cast<TInt64Builder&>(*Children_.at(field).Get()); +} + +TStringBuilder& TMapBuilder::StringAt(const TString& field) { + return static_cast<TStringBuilder&>(*Children_.at(field).Get()); +} + +TBoolBuilder& TMapBuilder::BoolAt(const TString& field) { + return static_cast<TBoolBuilder&>(*Children_.at(field).Get()); +} + +TMapValidator TMapBuilder::CreateValidator() { + THashMap<TString, TSimpleSharedPtr<TValidator>> children; + for (const auto& [name, builderPtr] : Children_) { + children[name] = NDetail::CreateValidatorPtr(builderPtr); + } + + return TMapValidator(std::move(children), Opaque_); +} + + +// TArrayBuilder +TArrayBuilder::TArrayBuilder() + : TCommonBuilderOps<TArrayBuilder>(EBuilderType::Array) {} + +TArrayBuilder::TArrayBuilder(const TArrayBuilder& builder) + : TCommonBuilderOps<TArrayBuilder>(builder) + , ItemPtr_(builder.ItemPtr_) + , Unique_(builder.Unique_) {} + +TArrayBuilder::TArrayBuilder(TArrayBuilder&& builder) + : TCommonBuilderOps<TArrayBuilder>(std::move(builder)) + , ItemPtr_(std::move(builder.ItemPtr_)) + , Unique_(builder.Unique_) {} + +TArrayBuilder& TArrayBuilder::operator=(const TArrayBuilder& builder) { + TBuilder::operator=(builder); + ItemPtr_ = builder.ItemPtr_; + Unique_ = builder.Unique_; + return *this; +} + +TArrayBuilder& TArrayBuilder::operator=(TArrayBuilder&& builder) { + TBuilder::operator=(std::move(builder)); + ItemPtr_ = std::move(builder.ItemPtr_); + Unique_ = builder.Unique_; + return *this; +} + +TArrayBuilder::TArrayBuilder(std::function<void(TArrayBuilder&)> configurator) + :TArrayBuilder() { + configurator(*this); +} + +TArrayBuilder& TArrayBuilder::Unique() { + Unique_ = true; + return *this; +} + +TArrayBuilder& TArrayBuilder::MapItem(std::function<void(TMapBuilder&)> configurator) { + Item(TMapBuilder(configurator)); + return *this; +} + +TArrayBuilder& TArrayBuilder::ArrayItem(std::function<void(TArrayBuilder&)> configurator) { + Item(TArrayBuilder(configurator)); + return *this; +} + +TArrayBuilder& TArrayBuilder::Int64Item(std::function<void(TInt64Builder&)> configurator) { + Item(TInt64Builder(configurator)); + return *this; +} + +TArrayBuilder& TArrayBuilder::StringItem(std::function<void(TStringBuilder&)> configurator) { + Item(TStringBuilder(configurator)); + return *this; +} + +TArrayBuilder& TArrayBuilder::BoolItem(std::function<void(TBoolBuilder&)> configurator) { + Item(TBoolBuilder(configurator)); + return *this; +} + +NDetail::TBuilder& TArrayBuilder::GetItem() { + if (ItemPtr_.Get() == nullptr) { + ythrow BuilderException() << "There is no item builder yet"; + } + return *ItemPtr_.Get(); +} + +TArrayValidator TArrayBuilder::CreateValidator() { + return TArrayValidator(NDetail::CreateValidatorPtr(ItemPtr_), Unique_); +} + + +// TInt64Builder +TInt64Builder::TInt64Builder() + : TCommonBuilderOps<TInt64Builder>(EBuilderType::Int64) {} + +TInt64Builder::TInt64Builder(const TInt64Builder& builder) + : TCommonBuilderOps<TInt64Builder>(builder) + , Min_(builder.Min_) + , Max_(builder.Max_) {} + +TInt64Builder::TInt64Builder(TInt64Builder&& builder) + : TCommonBuilderOps<TInt64Builder>(std::move(builder)) + , Min_(builder.Min_) + , Max_(builder.Max_) {} + +TInt64Builder& TInt64Builder::operator=(const TInt64Builder& builder) { + TBuilder::operator=(builder); + Min_ = builder.Min_; + Max_ = builder.Max_; + return *this; +} + +TInt64Builder& TInt64Builder::operator=(TInt64Builder&& builder) { + TBuilder::operator=(std::move(builder)); + Min_ = builder.Min_; + Max_ = builder.Max_; + return *this; +} + +TInt64Builder::TInt64Builder(std::function<void(TInt64Builder&)> configurator) + :TInt64Builder() { + configurator(*this); +} + +TInt64Builder& TInt64Builder::Min(i64 min) { + Min_ = min; + return *this; +} + +TInt64Builder& TInt64Builder::Max(i64 max) { + Max_ = max; + return *this; +} + +TInt64Builder& TInt64Builder::Range(i64 min, i64 max) { + Max_ = max; + Min_ = min; + return *this; +} + +TInt64Validator TInt64Builder::CreateValidator() { + return TInt64Validator(Min_, Max_); +} + + +// TStringBuilder +TStringBuilder::TStringBuilder() + : TCommonBuilderOps<TStringBuilder>(EBuilderType::Int64) {} + +TStringBuilder::TStringBuilder(const TStringBuilder& builder) + : TCommonBuilderOps<TStringBuilder>(builder) {} + +TStringBuilder::TStringBuilder(TStringBuilder&& builder) + : TCommonBuilderOps<TStringBuilder>(std::move(builder)) {} + +TStringBuilder& TStringBuilder::operator=(const TStringBuilder& builder) { + TBuilder::operator=(builder); + return *this; +} + +TStringBuilder& TStringBuilder::operator=(TStringBuilder&& builder) { + TBuilder::operator=(std::move(builder)); + return *this; +} + +TStringBuilder::TStringBuilder(std::function<void(TStringBuilder&)> configurator) + :TStringBuilder() { + configurator(*this); +} + +TStringValidator TStringBuilder::CreateValidator() { + return TStringValidator(); +} + + +// TBoolBuilder +TBoolBuilder::TBoolBuilder() + : TCommonBuilderOps<TBoolBuilder>(EBuilderType::Bool) {} + +TBoolBuilder::TBoolBuilder(const TBoolBuilder& builder) + : TCommonBuilderOps<TBoolBuilder>(builder) {} + +TBoolBuilder::TBoolBuilder(TBoolBuilder&& builder) + : TCommonBuilderOps<TBoolBuilder>(std::move(builder)) {} + +TBoolBuilder& TBoolBuilder::operator=(const TBoolBuilder& builder) { + TBuilder::operator=(builder); + return *this; +} + +TBoolBuilder& TBoolBuilder::operator=(TBoolBuilder&& builder) { + TBuilder::operator=(std::move(builder)); + return *this; +} + +TBoolBuilder::TBoolBuilder(std::function<void(TBoolBuilder&)> configurator) + :TBoolBuilder() { + configurator(*this); +} + +TBoolValidator TBoolBuilder::CreateValidator() { + return TBoolValidator(); +} + +} // namespace NYamlConfig::NValidator diff --git a/ydb/library/yaml_config/validator/validator_builder.h b/ydb/library/yaml_config/validator/validator_builder.h new file mode 100644 index 00000000000..eee7b94912a --- /dev/null +++ b/ydb/library/yaml_config/validator/validator_builder.h @@ -0,0 +1,292 @@ +#pragma once + +#include "validator.h" + +#include <util/generic/ptr.h> +#include <util/generic/vector.h> +#include <util/generic/string.h> +#include <util/generic/hash.h> +#include <util/string/cast.h> +#include <util/system/types.h> +#include <util/generic/yexception.h> +#include <util/generic/ylimits.h> + +#include <functional> + +namespace NYamlConfig::NValidator { + +enum class EBuilderType { + Generic, Map, Array, Int64, String, Bool +}; + +class BuilderException : public yexception {}; + +class TGenericBuilder; +class TMapBuilder; +class TArrayBuilder; +class TInt64Builder; +class TStringBuilder; +class TBoolBuilder; + +namespace NDetail { + +class TBuilder; + +TSimpleSharedPtr<TValidator> CreateValidatorPtr(const TSimpleSharedPtr<TBuilder>& builder); + +class TBuilder { + friend TSimpleSharedPtr<TValidator> NDetail::CreateValidatorPtr(const TSimpleSharedPtr<TBuilder>& builder); + +public: + TBuilder(EBuilderType builderType); + + TBuilder(const TBuilder& builder) = default; + TBuilder(TBuilder&& builder) = default; + + TBuilder& operator=(const TBuilder& builder); + TBuilder& operator=(TBuilder&& builder); + + virtual ~TBuilder() = default; + +protected: + const EBuilderType BuilderType_; + bool Required_ = true; + TString Description_; +}; + +template <typename ThisType> +class TCommonBuilderOps : public TBuilder { +public: + TCommonBuilderOps<ThisType>(EBuilderType builderType); + TCommonBuilderOps<ThisType>(const TBuilder& builder); + + ThisType& Optional(); + ThisType& Required(); + ThisType& Description(const TString& description); + + ThisType& Configure(std::function<void(ThisType&)> configurator = [](auto&){}); + +private: + ThisType& AsDerived(); +}; + +} // namespace NDetail + +class TGenericBuilder : public NDetail::TCommonBuilderOps<TGenericBuilder> { +public: + TGenericBuilder(); + + TGenericBuilder(const TGenericBuilder& builder); + TGenericBuilder(TGenericBuilder&& builder); + + TGenericBuilder& operator=(const TGenericBuilder& builder); + TGenericBuilder& operator=(TGenericBuilder&& builder); + + TGenericBuilder(std::function<void(TGenericBuilder&)> configurator); + + template <typename Builder> + TGenericBuilder& CanBe(Builder builder); + TGenericBuilder& CanBeMap(std::function<void(TMapBuilder&)> configurator = [](auto&){}); + TGenericBuilder& CanBeArray(std::function<void(TArrayBuilder&)> configurator = [](auto&){}); + TGenericBuilder& CanBeInt64(std::function<void(TInt64Builder&)> configurator = [](auto&){}); + TGenericBuilder& CanBeString(std::function<void(TStringBuilder&)> configurator = [](auto&){}); + TGenericBuilder& CanBeBool(std::function<void(TBoolBuilder&)> configurator = [](auto&){}); + + TGenericValidator CreateValidator(); + +private: + struct NodeTypeAndBuilder { + EBuilderType Type; + TSimpleSharedPtr<TBuilder> Builder; + + NodeTypeAndBuilder(); + NodeTypeAndBuilder(EBuilderType type, TSimpleSharedPtr<TBuilder> builder); + }; + + TVector<TSimpleSharedPtr<TBuilder>> PossibleBuilderPtrs_; +}; + +class TMapBuilder : public NDetail::TCommonBuilderOps<TMapBuilder> { +public: + TMapBuilder(); + + TMapBuilder(const TMapBuilder& builder); + TMapBuilder(TMapBuilder&& builder); + + TMapBuilder& operator=(const TMapBuilder& builder); + TMapBuilder& operator=(TMapBuilder&& builder); + + TMapBuilder(std::function<void(TMapBuilder&)> configurator); + + template <typename Builder> + TMapBuilder& Field(const TString& field, Builder builder); + + TMapBuilder& GenericField(const TString& field, std::function<void(TGenericBuilder&)> configurator = [](auto&){}); + TMapBuilder& Map(const TString& field, std::function<void(TMapBuilder&)> configurator = [](auto&){}); + TMapBuilder& Array(const TString& field, std::function<void(TArrayBuilder&)> configurator = [](auto&){}); + TMapBuilder& Int64(const TString& field, std::function<void(TInt64Builder&)> configurator = [](auto&){}); + TMapBuilder& String(const TString& field, std::function<void(TStringBuilder&)> configurator = [](auto&){}); + TMapBuilder& Bool(const TString& field, std::function<void(TBoolBuilder&)> configurator = [](auto&){}); + + TMapBuilder& Opaque(); + TMapBuilder& NotOpaque(); + + bool HasField(const TString& field); + + TGenericBuilder& GenericFieldAt(const TString& field); + TMapBuilder& MapAt(const TString& field); + TArrayBuilder& ArrayAt(const TString& field); + TInt64Builder& Int64At(const TString& field); + TStringBuilder& StringAt(const TString& field); + TBoolBuilder& BoolAt(const TString& field); + + TMapValidator CreateValidator(); + +private: + THashMap<TString, TSimpleSharedPtr<TBuilder>> Children_; + bool Opaque_ = false; + + void ThrowIfAlreadyHasField(const TString& field); +}; + +class TArrayBuilder : public NDetail::TCommonBuilderOps<TArrayBuilder> { +public: + TArrayBuilder(); + + TArrayBuilder(const TArrayBuilder& builder); + TArrayBuilder(TArrayBuilder&& builder); + + TArrayBuilder& operator=(const TArrayBuilder& builder); + TArrayBuilder& operator=(TArrayBuilder&& builder); + + TArrayBuilder(std::function<void(TArrayBuilder&)> configurator); + + TArrayBuilder& Unique(); + + template <typename Builder> + TArrayBuilder& Item(Builder builder); + TArrayBuilder& MapItem(std::function<void(TMapBuilder&)> configurator = [](auto&){}); + TArrayBuilder& ArrayItem(std::function<void(TArrayBuilder&)> configurator = [](auto&){}); + TArrayBuilder& Int64Item(std::function<void(TInt64Builder&)> configurator = [](auto&){}); + TArrayBuilder& StringItem(std::function<void(TStringBuilder&)> configurator = [](auto&){}); + TArrayBuilder& BoolItem(std::function<void(TBoolBuilder&)> configurator = [](auto&){}); + + TBuilder& GetItem(); + + TArrayValidator CreateValidator(); + +private: + TSimpleSharedPtr<TBuilder> ItemPtr_; + bool Unique_ = false; +}; + +class TInt64Builder : public NDetail::TCommonBuilderOps<TInt64Builder> { +public: + TInt64Builder(); + + TInt64Builder(const TInt64Builder& builder); + TInt64Builder(TInt64Builder&& builder); + + TInt64Builder& operator=(const TInt64Builder& builder); + TInt64Builder& operator=(TInt64Builder&& builder); + + TInt64Builder(std::function<void(TInt64Builder&)> configurator); + + TInt64Builder& Min(i64 min); + TInt64Builder& Max(i64 max); + TInt64Builder& Range(i64 min, i64 max); + + TInt64Validator CreateValidator(); + +private: + i64 Min_ = ::Min<i64>(); + i64 Max_ = ::Max<i64>(); +}; + +class TStringBuilder : public NDetail::TCommonBuilderOps<TStringBuilder> { +public: + TStringBuilder(); + + TStringBuilder(const TStringBuilder& builder); + TStringBuilder(TStringBuilder&& builder); + + TStringBuilder& operator=(const TStringBuilder& builder); + TStringBuilder& operator=(TStringBuilder&& builder); + + TStringBuilder(std::function<void(TStringBuilder&)> configurator); + + TStringValidator CreateValidator(); +}; + +class TBoolBuilder : public NDetail::TCommonBuilderOps<TBoolBuilder> { +public: + TBoolBuilder(); + + TBoolBuilder(const TBoolBuilder& builder); + TBoolBuilder(TBoolBuilder&& builder); + + TBoolBuilder& operator=(const TBoolBuilder& builder); + TBoolBuilder& operator=(TBoolBuilder&& builder); + + TBoolBuilder(std::function<void(TBoolBuilder&)> configurator); + + TBoolValidator CreateValidator(); +}; + +template <typename ThisType> +NDetail::TCommonBuilderOps<ThisType>::TCommonBuilderOps(EBuilderType builderType) + : TBuilder(builderType) {} + +template <typename ThisType> +NDetail::TCommonBuilderOps<ThisType>::TCommonBuilderOps(const TBuilder& builder) + : TBuilder(builder) {} + +template <typename ThisType> +ThisType& NDetail::TCommonBuilderOps<ThisType>::AsDerived() { + return static_cast<ThisType&>(*this); +} + +template <typename ThisType> +ThisType& NDetail::TCommonBuilderOps<ThisType>::Optional() { + Required_ = false; + return AsDerived(); +} + +template <typename ThisType> +ThisType& NDetail::TCommonBuilderOps<ThisType>::Required() { + Required_ = true; + return AsDerived(); +} + +template <typename ThisType> +ThisType& NDetail::TCommonBuilderOps<ThisType>::Description(const TString& description) { + Description_ = description; + return AsDerived(); +} + +template <typename ThisType> +ThisType& NDetail::TCommonBuilderOps<ThisType>::Configure(std::function<void(ThisType&)> configurator) { + configurator(AsDerived()); + return AsDerived(); +} + +template <typename Builder> +TGenericBuilder& TGenericBuilder::CanBe(Builder builder) { + PossibleBuilderPtrs_.emplace_back(new Builder(std::move(builder))); + return *this; +} + +template <typename Builder> +TMapBuilder& TMapBuilder::Field(const TString& field, Builder builder) { + ThrowIfAlreadyHasField(field); + Children_[field] = TSimpleSharedPtr<TBuilder>(new Builder(std::move(builder))); + return *this; +} + +template <typename Builder> +TArrayBuilder& TArrayBuilder::Item(Builder builder) { + ItemPtr_ = MakeSimpleShared<Builder>(std::move(builder)); + return *this; +} + +} // namespace NYamlConfig::NValidator diff --git a/ydb/library/yaml_config/validator/ya.make b/ydb/library/yaml_config/validator/ya.make new file mode 100644 index 00000000000..d6bf8e3449b --- /dev/null +++ b/ydb/library/yaml_config/validator/ya.make @@ -0,0 +1,20 @@ +LIBRARY() + +SRCS( + validator_builder.h + validator_builder.cpp + validator.h + validator.cpp +) + +PEERDIR( + library/cpp/yaml/fyamlcpp +) + +GENERATE_ENUM_SERIALIZATION(validator_builder.h) + +END() + +RECURSE_FOR_TESTS( + ut +) diff --git a/ydb/library/yaml_config/ya.make b/ydb/library/yaml_config/ya.make index 3e9602ba3e1..637f71bbd9c 100644 --- a/ydb/library/yaml_config/ya.make +++ b/ydb/library/yaml_config/ya.make @@ -27,6 +27,7 @@ END() RECURSE( public + validator ) RECURSE_FOR_TESTS( |