Skip to content

Commit 5f5fb69

Browse files
coteeqblinkov
authored andcommitted
YT-20778: Allow YsonStructs to be validated before the update
commit_hash:e26a48890dd22d3840070a0a33095d1b8d4d35d8
1 parent 4c28552 commit 5f5fb69

File tree

3 files changed

+128
-6
lines changed

3 files changed

+128
-6
lines changed

yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,5 +176,36 @@ TEST(TUpdateYsonStructTest, Nested)
176176
EXPECT_EQ(updatedCommand, "sort");
177177
}
178178

179+
TEST(TUpdateYsonStructTest, Validate)
180+
{
181+
auto oldSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=pool;}")));
182+
auto longPoolSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=new_pool;}")));
183+
auto shortPoolSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=p;}")));
184+
185+
std::string updatedPool;
186+
187+
auto configurator = TConfigurator<TSpecWithPool>();
188+
configurator.Field("pool", &TSpecWithPool::Pool)
189+
.Validator(BIND([&] (const std::string& newPool) {
190+
THROW_ERROR_EXCEPTION_IF(
191+
newPool.size() > 4,
192+
"Pool name too long");
193+
}))
194+
.Updater(BIND([&] (const std::string& newPool) {
195+
updatedPool = newPool;
196+
}));
197+
198+
auto sealed = std::move(configurator).Seal();
199+
200+
EXPECT_THROW_WITH_SUBSTRING(
201+
sealed.Validate(oldSpec, longPoolSpec),
202+
"Pool name too long");
203+
204+
sealed.Validate(oldSpec, shortPoolSpec);
205+
sealed.Update(oldSpec, shortPoolSpec);
206+
207+
EXPECT_EQ(updatedPool, "p");
208+
}
209+
179210
} // namespace
180211
} // namespace NYT::NYTree

yt/yt/core/ytree/yson_struct_update-inl.h

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,36 @@ struct TUnwrapYsonStructIntrusivePtr<TIntrusivePtr<T>>
4848

4949
////////////////////////////////////////////////////////////////////////////////
5050

51+
template <class TValue>
52+
TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Validator(TCallback<void(const TValue&, const TValue&)> validator)
53+
{
54+
VerifyEmptyValidator();
55+
Validator_ = validator;
56+
return *this;
57+
}
58+
59+
template <class TValue>
60+
TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Validator(TCallback<void(const TValue&)> validator)
61+
{
62+
VerifyEmptyValidator();
63+
Validator_ = BIND_NO_PROPAGATE([validator = std::move(validator)] (const TValue& /*oldValue*/, const TValue& newValue) {
64+
validator(std::move(newValue));
65+
});
66+
return *this;
67+
}
68+
5169
template <class TValue>
5270
TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const TValue&, const TValue&)> updater)
5371
{
54-
VerifyEmpty();
72+
VerifyEmptyUpdater();
5573
Updater_ = updater;
5674
return *this;
5775
}
5876

5977
template <class TValue>
6078
TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const TValue&)> updater)
6179
{
62-
VerifyEmpty();
80+
VerifyEmptyUpdater();
6381
Updater_ = BIND_NO_PROPAGATE([updater = std::move(updater)] (const TValue& /*oldValue*/, const TValue& newValue) {
6482
updater(std::move(newValue));
6583
});
@@ -90,14 +108,31 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::NestedUpdater(
90108
TUnwrappedValue,
91109
typename NDetail::TUnwrapYsonStructIntrusivePtr<TValue>::TStruct>);
92110

93-
VerifyEmpty();
111+
VerifyEmptyUpdater();
94112
auto configurator = configureCallback();
95113
Updater_ = BIND_NO_PROPAGATE([configurator = std::move(configurator)] (const TValue& oldValue, const TValue& newValue) {
96114
configurator.Update(oldValue, newValue);
97115
});
98116
return *this;
99117
}
100118

119+
template <class TValue>
120+
void TFieldRegistrar<TValue>::DoValidate(
121+
IYsonStructParameterPtr parameter,
122+
TYsonStructBase* oldStruct,
123+
TYsonStructBase* newStruct) const
124+
{
125+
if (!Validator_) {
126+
return;
127+
}
128+
129+
auto typedParameter = DynamicPointerCast<TYsonStructParameter<TValue>>(parameter);
130+
YT_VERIFY(typedParameter);
131+
Validator_(
132+
typedParameter->GetValue(oldStruct),
133+
typedParameter->GetValue(newStruct));
134+
}
135+
101136
template <class TValue>
102137
void TFieldRegistrar<TValue>::DoUpdate(
103138
IYsonStructParameterPtr parameter,
@@ -116,7 +151,13 @@ void TFieldRegistrar<TValue>::DoUpdate(
116151
}
117152

118153
template <class TValue>
119-
void TFieldRegistrar<TValue>::VerifyEmpty() const
154+
void TFieldRegistrar<TValue>::VerifyEmptyValidator() const
155+
{
156+
YT_VERIFY(!Validator_);
157+
}
158+
159+
template <class TValue>
160+
void TFieldRegistrar<TValue>::VerifyEmptyUpdater() const
120161
{
121162
YT_VERIFY(!Updater_);
122163
}
@@ -169,17 +210,35 @@ TSealedConfigurator<TStruct> TConfigurator<TStruct>::Seal() &&
169210
{
170211
return std::move(*this);
171212
}
213+
172214
////////////////////////////////////////////////////////////////////////////////
173215

174216
template <CYsonStructDerived TStruct>
175217
TSealedConfigurator<TStruct>::TSealedConfigurator(TConfigurator<TStruct> configurator)
176218
: RegisteredFields_(std::move(configurator.RegisteredFields_))
177219
{ }
178220

221+
template <CYsonStructDerived TStruct>
222+
void TSealedConfigurator<TStruct>::Validate(
223+
TIntrusivePtr<TStruct> oldStruct,
224+
TIntrusivePtr<TStruct> newStruct) const
225+
{
226+
Do(oldStruct, newStruct, &NDetail::IFieldRegistrar::DoValidate);
227+
}
228+
179229
template <CYsonStructDerived TStruct>
180230
void TSealedConfigurator<TStruct>::Update(
181231
TIntrusivePtr<TStruct> oldStruct,
182232
TIntrusivePtr<TStruct> newStruct) const
233+
{
234+
Do(oldStruct, newStruct, &NDetail::IFieldRegistrar::DoUpdate);
235+
}
236+
237+
template <CYsonStructDerived TStruct>
238+
void TSealedConfigurator<TStruct>::Do(
239+
TIntrusivePtr<TStruct> oldStruct,
240+
TIntrusivePtr<TStruct> newStruct,
241+
TFieldRegistrarMethod fieldMethod) const
183242
{
184243
const auto* meta = oldStruct->GetMeta();
185244
YT_VERIFY(meta == newStruct->GetMeta());
@@ -194,7 +253,7 @@ void TSealedConfigurator<TStruct>::Update(
194253
if (fieldDescIter == parameterToFieldRegistrar.end()) {
195254
THROW_ERROR_EXCEPTION("Field %Qv is not marked as updatable, but was changed", name);
196255
} else {
197-
fieldDescIter->second->DoUpdate(parameter, oldStruct.Get(), newStruct.Get());
256+
(*(fieldDescIter->second).*fieldMethod)(parameter, oldStruct.Get(), newStruct.Get());
198257
}
199258
}
200259
}

yt/yt/core/ytree/yson_struct_update.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ DECLARE_REFCOUNTED_STRUCT(TRegisteredFieldDirectory);
2626
struct IFieldRegistrar
2727
: public TRefCounted
2828
{
29+
virtual void DoValidate(
30+
IYsonStructParameterPtr parameter,
31+
TYsonStructBase* oldStruct,
32+
TYsonStructBase* newStruct) const = 0;
33+
2934
virtual void DoUpdate(
3035
IYsonStructParameterPtr parameter,
3136
TYsonStructBase* oldStruct,
@@ -42,6 +47,12 @@ class TFieldRegistrar
4247
: public IFieldRegistrar
4348
{
4449
public:
50+
// Registers validator that accepts old and new values as arguments.
51+
TFieldRegistrar& Validator(TCallback<void(const TValue&, const TValue&)> validator);
52+
53+
// Registers validator that accepts only new value as an argument.
54+
TFieldRegistrar& Validator(TCallback<void(const TValue&)> validator);
55+
4556
// Registers updater that accepts old and new values as arguments.
4657
TFieldRegistrar& Updater(TCallback<void(const TValue&, const TValue&)> updater);
4758

@@ -58,10 +69,17 @@ class TFieldRegistrar
5869
TYsonStructBase* oldStruct,
5970
TYsonStructBase* newStruct) const override;
6071

72+
void DoValidate(
73+
IYsonStructParameterPtr parameter,
74+
TYsonStructBase* oldStruct,
75+
TYsonStructBase* newStruct) const override;
76+
6177
private:
62-
void VerifyEmpty() const;
78+
void VerifyEmptyUpdater() const;
79+
void VerifyEmptyValidator() const;
6380

6481
TCallback<void(const TValue&, const TValue&)> Updater_;
82+
TCallback<void(const TValue&, const TValue&)> Validator_;
6583
};
6684

6785
////////////////////////////////////////////////////////////////////////////////
@@ -114,11 +132,25 @@ class TSealedConfigurator
114132
public:
115133
TSealedConfigurator(TConfigurator<TStruct> configurator);
116134

135+
void Validate(
136+
TIntrusivePtr<TStruct> oldStruct,
137+
TIntrusivePtr<TStruct> newStruct) const;
138+
117139
void Update(
118140
TIntrusivePtr<TStruct> oldStruct,
119141
TIntrusivePtr<TStruct> newStruct) const;
120142

121143
private:
144+
using TFieldRegistrarMethod = void(NDetail::IFieldRegistrar::*)(
145+
IYsonStructParameterPtr parameter,
146+
TYsonStructBase* oldStruct,
147+
TYsonStructBase* newStruct) const;
148+
149+
void Do(
150+
TIntrusivePtr<TStruct> oldStruct,
151+
TIntrusivePtr<TStruct> newStruct,
152+
TFieldRegistrarMethod fieldMethod) const;
153+
122154
NDetail::TRegisteredFieldDirectoryPtr RegisteredFields_;
123155
};
124156

0 commit comments

Comments
 (0)