|
| 1 | +#include "external_source_builder.h" |
| 2 | +#include "validation_functions.h" |
| 3 | + |
| 4 | +#include <util/string/join.h> |
| 5 | +#include <ydb/core/protos/flat_scheme_op.pb.h> |
| 6 | + |
| 7 | +namespace NKikimr::NExternalSource { |
| 8 | +namespace { |
| 9 | + |
| 10 | +class TValidatedExternalDataSource final : public IExternalSource { |
| 11 | +public: |
| 12 | + TValidatedExternalDataSource( |
| 13 | + const TString& name, |
| 14 | + const std::vector<TExternalSourceBuilder::TAuthHolder>& authMethods, |
| 15 | + const std::unordered_map<TString, TExternalSourceBuilder::TConditionalValidator>& availableProperties, |
| 16 | + const std::vector<TRegExMatch>& hostnamePatterns) |
| 17 | + : Name_(name) |
| 18 | + , AuthMethodsForCheck_(authMethods) |
| 19 | + , AvailableProperties_(availableProperties) |
| 20 | + , HostnamePatterns_(hostnamePatterns) |
| 21 | + { |
| 22 | + |
| 23 | + } |
| 24 | + |
| 25 | + virtual TString Pack(const NKikimrExternalSources::TSchema&, |
| 26 | + const NKikimrExternalSources::TGeneral&) const override { |
| 27 | + ythrow TExternalSourceException() << "Internal error. Only external table supports pack operation"; |
| 28 | + } |
| 29 | + |
| 30 | + virtual TString GetName() const override { |
| 31 | + return Name_; |
| 32 | + } |
| 33 | + |
| 34 | + virtual bool HasExternalTable() const override { |
| 35 | + return false; |
| 36 | + } |
| 37 | + |
| 38 | + virtual TVector<TString> GetAuthMethods() const override { |
| 39 | + TVector<TString> result; |
| 40 | + |
| 41 | + for (auto a : AuthMethodsForCheck_) { |
| 42 | + result.push_back(a.Auth); |
| 43 | + } |
| 44 | + |
| 45 | + return result; |
| 46 | + } |
| 47 | + |
| 48 | + TVector<TString> GetAuthMethods(const TString& externalDataSourceDescription) const { |
| 49 | + NKikimrSchemeOp::TExternalDataSourceDescription proto; |
| 50 | + |
| 51 | + if (!proto.ParseFromString(externalDataSourceDescription)) { |
| 52 | + ythrow TExternalSourceException() |
| 53 | + << "Internal error. " |
| 54 | + << "Couldn't parse protobuf with external data source description"; |
| 55 | + } |
| 56 | + |
| 57 | + TVector<TString> result; |
| 58 | + |
| 59 | + for (auto a : AuthMethodsForCheck_) { |
| 60 | + if (a.UseCondition(proto.GetProperties().GetProperties())) { |
| 61 | + result.push_back(a.Auth); |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + return result; |
| 66 | + } |
| 67 | + |
| 68 | + virtual TMap<TString, TVector<TString>> GetParameters(const TString&) const override { |
| 69 | + ythrow TExternalSourceException() << "Internal error. Only external table supports parameters"; |
| 70 | + } |
| 71 | + |
| 72 | + virtual void ValidateExternalDataSource(const TString& externalDataSourceDescription) const override { |
| 73 | + NKikimrSchemeOp::TExternalDataSourceDescription proto; |
| 74 | + |
| 75 | + if (!proto.ParseFromString(externalDataSourceDescription)) { |
| 76 | + ythrow TExternalSourceException() |
| 77 | + << "Internal error. " |
| 78 | + << "Couldn't parse protobuf with external data source description"; |
| 79 | + } |
| 80 | + |
| 81 | + auto properties = proto.GetProperties().GetProperties(); |
| 82 | + std::unordered_set<TString> validatedProperties; |
| 83 | + |
| 84 | + for (const auto& [key, value] : properties) { |
| 85 | + auto p = AvailableProperties_.find(key); |
| 86 | + |
| 87 | + if (AvailableProperties_.end() == p) { |
| 88 | + throw TExternalSourceException() << "Unsupported property: " << key; |
| 89 | + } |
| 90 | + |
| 91 | + // validate property value |
| 92 | + if (p->second.ApplyCondition(properties)) { |
| 93 | + p->second.Validator(key, value); |
| 94 | + } |
| 95 | + |
| 96 | + validatedProperties.emplace(key); |
| 97 | + } |
| 98 | + |
| 99 | + // validate properties that has been left |
| 100 | + for (const auto& [property, validator] : AvailableProperties_) { |
| 101 | + if (validatedProperties.contains(property)) { |
| 102 | + continue; |
| 103 | + } |
| 104 | + |
| 105 | + if (validator.ApplyCondition(properties)) { |
| 106 | + validator.Validator(property, ""); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + ValidateHostname(HostnamePatterns_, proto.GetLocation()); |
| 111 | + } |
| 112 | + |
| 113 | + virtual NThreading::TFuture<std::shared_ptr<TMetadata>> LoadDynamicMetadata(std::shared_ptr<TMetadata> meta) override { |
| 114 | + return NThreading::MakeFuture(std::move(meta)); |
| 115 | + } |
| 116 | + |
| 117 | + virtual bool CanLoadDynamicMetadata() const override { |
| 118 | + return false; |
| 119 | + } |
| 120 | + |
| 121 | +private: |
| 122 | + const TString Name_; |
| 123 | + const std::vector<TExternalSourceBuilder::TAuthHolder> AuthMethodsForCheck_; |
| 124 | + const std::unordered_map<TString, TExternalSourceBuilder::TConditionalValidator> AvailableProperties_; |
| 125 | + const std::vector<TRegExMatch> HostnamePatterns_; |
| 126 | +}; |
| 127 | + |
| 128 | +} // unnamed |
| 129 | + |
| 130 | +TExternalSourceBuilder::TExternalSourceBuilder(const TString& name) |
| 131 | + : Name_(name) |
| 132 | +{ |
| 133 | +} |
| 134 | + |
| 135 | +TExternalSourceBuilder& TExternalSourceBuilder::Auth(const TVector<TString>& authMethods, TCondition condition) { |
| 136 | + for (auto a : authMethods) { |
| 137 | + AuthMethodsForCheck_.push_back(TExternalSourceBuilder::TAuthHolder{a, condition}); |
| 138 | + } |
| 139 | + |
| 140 | + return *this; |
| 141 | +} |
| 142 | + |
| 143 | +TExternalSourceBuilder& TExternalSourceBuilder::Property(TString name, TValidator validator, TCondition condition) { |
| 144 | + AvailableProperties_.emplace(name, TExternalSourceBuilder::TConditionalValidator{validator, condition}); |
| 145 | + return *this; |
| 146 | +} |
| 147 | + |
| 148 | +TExternalSourceBuilder& TExternalSourceBuilder::Properties(const TSet<TString>& availableProperties, TValidator validator, TCondition condition) { |
| 149 | + for (auto p : availableProperties) { |
| 150 | + Property(p, validator, condition); |
| 151 | + } |
| 152 | + |
| 153 | + return *this; |
| 154 | +} |
| 155 | + |
| 156 | +TExternalSourceBuilder& TExternalSourceBuilder::HostnamePatterns(const std::vector<TRegExMatch>& patterns) { |
| 157 | + HostnamePatterns_.insert( |
| 158 | + HostnamePatterns_.end(), patterns.begin(), patterns.end()); |
| 159 | + return *this; |
| 160 | +} |
| 161 | + |
| 162 | +IExternalSource::TPtr TExternalSourceBuilder::Build() { |
| 163 | + return MakeIntrusive<TValidatedExternalDataSource>( |
| 164 | + std::move(Name_), std::move(AuthMethodsForCheck_), std::move(AvailableProperties_), std::move(HostnamePatterns_)); |
| 165 | +} |
| 166 | + |
| 167 | +TCondition GetHasSettingCondition(const TString& property, const TString& value) { |
| 168 | + return [property, value](const ::google::protobuf::Map<TProtoStringType, TProtoStringType>& properties) -> bool { |
| 169 | + auto it = properties.find(property); |
| 170 | + return properties.end() != it && value == it->second; |
| 171 | + }; |
| 172 | +} |
| 173 | + |
| 174 | +TValidator GetRequiredValidator() { |
| 175 | + return [](const TString& property, const TString& value){ |
| 176 | + if (!value.empty()) { |
| 177 | + return; |
| 178 | + } |
| 179 | + |
| 180 | + throw TExternalSourceException() << "required property: " << property << " is not set"; |
| 181 | + }; |
| 182 | +} |
| 183 | + |
| 184 | +TValidator GetIsInListValidator(const std::unordered_set<TString>& values, bool required) { |
| 185 | + auto joinedValues = JoinSeq(", ", values); |
| 186 | + |
| 187 | + return [values, required, joinedValues](const TString& property, const TString& value){ |
| 188 | + if (value.empty() && required) { |
| 189 | + throw TExternalSourceException() << " required property: " << property << " is not set"; |
| 190 | + } |
| 191 | + |
| 192 | + if (value.empty()) { |
| 193 | + return; |
| 194 | + } |
| 195 | + |
| 196 | + if (!values.contains(value)) { |
| 197 | + throw TExternalSourceException() |
| 198 | + << " property: " << property |
| 199 | + << " has wrong value: " << value |
| 200 | + << " allowed values: " << joinedValues; |
| 201 | + } |
| 202 | + }; |
| 203 | +} |
| 204 | + |
| 205 | +} // NKikimr::NExternalSource |
0 commit comments