Jsonnet 库中的复杂验证
Complex validation in Jsonnet library
我想做的是创建一个 libsonnet 库,对输入进行一些复杂的验证,但我不确定如何在 libsonnet
文件中实现它而不获取 null
返回。
我正在尝试使用 Jsonnet 为 Hosted Graphite's Alerts API 生成 API 调用。这个想法是我们可以将所有警报存储在版本控制中,并在 CI / CD 管道中更新它们。我想防止错误,所以我根据上面 API 规范的定义实施了一些复杂的验证。我将以下内容保存为 alerts.libsonnet
:
local alert_criteria_types = [
'above',
'below',
'missing',
'outside_bounds',
];
local notification_types_strings = [
'state_change',
];
local notification_types_arrays = [
'every',
'state_change',
];
local on_query_failure_types = [
'ignore',
'notify',
null,
];
{
local HostedGraphiteAlerts = self,
new(
name,
metric,
alert_criteria_type,
additional_alert_criteria={},
additional_criteria={},
expression='a',
scheduled_mutes=[],
notification_channels=['Email me'],
notification_type=null,
info=null,
on_query_failure='notify',
)::
// Simple checks
assert std.member(alert_criteria_types, alert_criteria_type) : "Input 'alert_criteria_type' is not one of the types: %s." % std.join(', ', alert_criteria_types);
assert std.member(on_query_failure_types, on_query_failure) : "Input 'on_query_failure_type' is not one of the types: %s." % std.join(', ', on_query_failure_types);
// Advanced checks
if notification_type != null && std.isString(notification_type) then
assert std.member(notification_types_strings, notification_type) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_strings);
if notification_type != null && std.isArray(notification_type) then
assert std.member(notification_types_arrays, notification_type[0]) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_arrays);
if notification_type != null && std.isArray(notification_type) then
assert std.member(notification_types_arrays, notification_type[0]) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_arrays);
if notification_type != null && std.isArray(notification_type) && notification_type[0] == 'every' then
assert notification_type[1] != null : "Input 'notification_type' cannot have an empty entry for 'time_in_minutes' for notification type 'every'.";
if notification_type != null && std.isArray(notification_type) && notification_type[0] == 'every' then
assert std.isNumber(notification_type[1]) : "Input 'notification_type' must have a JSON 'number' type for notification type 'every'.";
// Main
{
name: name,
metric: metric,
alert_criteria: {
type: alert_criteria_type,
} + additional_alert_criteria,
additional_criteria: additional_criteria,
expression: expression,
scheduled_mutes: scheduled_mutes,
notification_channels: notification_channels,
notification_type: notification_type,
info: info,
on_query_failure: on_query_failure,
},
}
这通过了基本的 jsonnetfmt
检查,但问题是当我像这样在 alerts.jsonnet
文件中使用它时:
local alerts = (import 'hosted_graphite.libsonnet').alerts;
alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
)
这简直returnsnull
:
$ jsonnet hosted_graphite/alerts.jsonnet
null
我知道这是因为它采用了第一个 assert
语句的值。但是还有什么办法可以做到这一点?
谢谢!
请注意 jsonnet
不是命令式语言,不要指望那些 if
行会像脚本的 where 部分一样被评估。
将断言视为必须始终评估为 true
的 "virtual" / 不可见字段
下面实现了(我认为)您所追求的:
hosted_graphite.libsonnet
local alert_criteria_types = [
'above',
'below',
'missing',
'outside_bounds',
];
local notification_types_strings = [
'state_change',
];
local notification_types_arrays = [
'every',
'state_change',
];
local on_query_failure_types = [
'ignore',
'notify',
null,
];
{
local HostedGraphiteAlerts = self,
new(
name,
metric,
alert_criteria_type,
additional_alert_criteria={},
additional_criteria={},
expression='a',
scheduled_mutes=[],
notification_channels=['Email me'],
notification_type=null,
info=null,
on_query_failure='notify',
)::
// Main
{
name: name,
metric: metric,
alert_criteria: {
type: alert_criteria_type,
} + additional_alert_criteria,
additional_criteria: additional_criteria,
expression: expression,
scheduled_mutes: scheduled_mutes,
notification_channels: notification_channels,
notification_type: notification_type,
info: info,
on_query_failure: on_query_failure,
// Simple checks
assert std.member(alert_criteria_types, self.alert_criteria.type) : (
"Input 'alert_criteria_type' is not one of the types: %s." % std.join(', ', alert_criteria_types)
),
assert std.member(on_query_failure_types, self.on_query_failure) : (
"Input 'on_query_failure_type' is not one of the types: %s." % std.join(', ', on_query_failure_types)
),
// Advanced checks:
// - 1st line is a conditional that must be false ('A||B' construct) to get 2nd line evaluated
// - 2nd line is the "final" type/value check, must be true
assert (self.notification_type == null || !std.isString(self.notification_type) ||
std.member(notification_types_strings, self.notification_type)) : (
"Input 'notification_type' string is not one of the types: %s." % std.join(', ', notification_types_strings)
),
assert (self.notification_type == null || !std.isArray(self.notification_type) ||
std.member(notification_types_arrays, self.notification_type[0])) : (
"Input 'notification_type' array is not one of the types: %s." % std.join(', ', notification_types_arrays)
),
assert (self.notification_type == null || !std.isArray(self.notification_type) ||
self.notification_type != ['every', null]) : (
"Input 'notification_type' cannot have an empty entry for 'time_in_minutes' for notification type 'every'."
),
assert (self.notification_type == null || !std.isArray(self.notification_type) ||
[self.notification_type[0], std.isNumber(self.notification_type[1])] == ['every', true]) : (
"Input 'notification_type' must have a JSON 'number' type for notification type 'every'."
),
},
}
alerts.jsonnet
local alerts = (import 'hosted_graphite.libsonnet');
{
a0: alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
),
a1: alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
notification_type='state_change',
),
a2: alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
notification_type=['every', 10],
),
}
请注意,我使用的是 self.<field>
而不是函数参数,这是一个允许 derivation/overriding 同时仍对断言进行评估的好模式。
顺便说一句,我还建议您查看 https://cuelang.org/,它与 jsonnet
在同一领域发挥作用,但是 类型检查是不可或缺的一部分语言。
我想做的是创建一个 libsonnet 库,对输入进行一些复杂的验证,但我不确定如何在 libsonnet
文件中实现它而不获取 null
返回。
我正在尝试使用 Jsonnet 为 Hosted Graphite's Alerts API 生成 API 调用。这个想法是我们可以将所有警报存储在版本控制中,并在 CI / CD 管道中更新它们。我想防止错误,所以我根据上面 API 规范的定义实施了一些复杂的验证。我将以下内容保存为 alerts.libsonnet
:
local alert_criteria_types = [
'above',
'below',
'missing',
'outside_bounds',
];
local notification_types_strings = [
'state_change',
];
local notification_types_arrays = [
'every',
'state_change',
];
local on_query_failure_types = [
'ignore',
'notify',
null,
];
{
local HostedGraphiteAlerts = self,
new(
name,
metric,
alert_criteria_type,
additional_alert_criteria={},
additional_criteria={},
expression='a',
scheduled_mutes=[],
notification_channels=['Email me'],
notification_type=null,
info=null,
on_query_failure='notify',
)::
// Simple checks
assert std.member(alert_criteria_types, alert_criteria_type) : "Input 'alert_criteria_type' is not one of the types: %s." % std.join(', ', alert_criteria_types);
assert std.member(on_query_failure_types, on_query_failure) : "Input 'on_query_failure_type' is not one of the types: %s." % std.join(', ', on_query_failure_types);
// Advanced checks
if notification_type != null && std.isString(notification_type) then
assert std.member(notification_types_strings, notification_type) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_strings);
if notification_type != null && std.isArray(notification_type) then
assert std.member(notification_types_arrays, notification_type[0]) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_arrays);
if notification_type != null && std.isArray(notification_type) then
assert std.member(notification_types_arrays, notification_type[0]) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_arrays);
if notification_type != null && std.isArray(notification_type) && notification_type[0] == 'every' then
assert notification_type[1] != null : "Input 'notification_type' cannot have an empty entry for 'time_in_minutes' for notification type 'every'.";
if notification_type != null && std.isArray(notification_type) && notification_type[0] == 'every' then
assert std.isNumber(notification_type[1]) : "Input 'notification_type' must have a JSON 'number' type for notification type 'every'.";
// Main
{
name: name,
metric: metric,
alert_criteria: {
type: alert_criteria_type,
} + additional_alert_criteria,
additional_criteria: additional_criteria,
expression: expression,
scheduled_mutes: scheduled_mutes,
notification_channels: notification_channels,
notification_type: notification_type,
info: info,
on_query_failure: on_query_failure,
},
}
这通过了基本的 jsonnetfmt
检查,但问题是当我像这样在 alerts.jsonnet
文件中使用它时:
local alerts = (import 'hosted_graphite.libsonnet').alerts;
alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
)
这简直returnsnull
:
$ jsonnet hosted_graphite/alerts.jsonnet
null
我知道这是因为它采用了第一个 assert
语句的值。但是还有什么办法可以做到这一点?
谢谢!
请注意 jsonnet
不是命令式语言,不要指望那些 if
行会像脚本的 where 部分一样被评估。
将断言视为必须始终评估为 true
下面实现了(我认为)您所追求的:
hosted_graphite.libsonnet
local alert_criteria_types = [
'above',
'below',
'missing',
'outside_bounds',
];
local notification_types_strings = [
'state_change',
];
local notification_types_arrays = [
'every',
'state_change',
];
local on_query_failure_types = [
'ignore',
'notify',
null,
];
{
local HostedGraphiteAlerts = self,
new(
name,
metric,
alert_criteria_type,
additional_alert_criteria={},
additional_criteria={},
expression='a',
scheduled_mutes=[],
notification_channels=['Email me'],
notification_type=null,
info=null,
on_query_failure='notify',
)::
// Main
{
name: name,
metric: metric,
alert_criteria: {
type: alert_criteria_type,
} + additional_alert_criteria,
additional_criteria: additional_criteria,
expression: expression,
scheduled_mutes: scheduled_mutes,
notification_channels: notification_channels,
notification_type: notification_type,
info: info,
on_query_failure: on_query_failure,
// Simple checks
assert std.member(alert_criteria_types, self.alert_criteria.type) : (
"Input 'alert_criteria_type' is not one of the types: %s." % std.join(', ', alert_criteria_types)
),
assert std.member(on_query_failure_types, self.on_query_failure) : (
"Input 'on_query_failure_type' is not one of the types: %s." % std.join(', ', on_query_failure_types)
),
// Advanced checks:
// - 1st line is a conditional that must be false ('A||B' construct) to get 2nd line evaluated
// - 2nd line is the "final" type/value check, must be true
assert (self.notification_type == null || !std.isString(self.notification_type) ||
std.member(notification_types_strings, self.notification_type)) : (
"Input 'notification_type' string is not one of the types: %s." % std.join(', ', notification_types_strings)
),
assert (self.notification_type == null || !std.isArray(self.notification_type) ||
std.member(notification_types_arrays, self.notification_type[0])) : (
"Input 'notification_type' array is not one of the types: %s." % std.join(', ', notification_types_arrays)
),
assert (self.notification_type == null || !std.isArray(self.notification_type) ||
self.notification_type != ['every', null]) : (
"Input 'notification_type' cannot have an empty entry for 'time_in_minutes' for notification type 'every'."
),
assert (self.notification_type == null || !std.isArray(self.notification_type) ||
[self.notification_type[0], std.isNumber(self.notification_type[1])] == ['every', true]) : (
"Input 'notification_type' must have a JSON 'number' type for notification type 'every'."
),
},
}
alerts.jsonnet
local alerts = (import 'hosted_graphite.libsonnet');
{
a0: alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
),
a1: alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
notification_type='state_change',
),
a2: alerts.new(
name='something',
metric='some.graphite.metric',
alert_criteria_type='below',
notification_type=['every', 10],
),
}
请注意,我使用的是 self.<field>
而不是函数参数,这是一个允许 derivation/overriding 同时仍对断言进行评估的好模式。
顺便说一句,我还建议您查看 https://cuelang.org/,它与 jsonnet
在同一领域发挥作用,但是 类型检查是不可或缺的一部分语言。