mirror of
https://git.proxmox.com/git/pve-common
synced 2025-08-09 14:21:31 +00:00
JSONSchema: add format validator support and cleanup check_format
Adds a third, optional parameter to register_format that allows specifying a function that will be called after parsing and can validate the parsed data. A validator should die on failed validation, and can also change the parsed object by returning a modified version of it. This is useful so one can register a format with its hash, thus allowing documentation to be generated automatically, while still enforcing certain validation rules. The validator only needs to be called in parse_property_string, since check_format always calls parse_property_string if there is a possibility of a validator existing at all. parse_property_string should then be called with named formats for best effect, as only then can validators be used. Clean up 'check_format' as well (which pretty much amounts to a rewrite). No existing functionality is intentionally changed. Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
parent
37c7daf0d4
commit
70fdc0501b
@ -121,19 +121,26 @@ register_standard_option('pve-snapshot-name', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
my $format_list = {};
|
my $format_list = {};
|
||||||
|
my $format_validators = {};
|
||||||
|
|
||||||
sub register_format {
|
sub register_format {
|
||||||
my ($format, $code) = @_;
|
my ($name, $format, $validator) = @_;
|
||||||
|
|
||||||
die "JSON schema format '$format' already registered\n"
|
die "JSON schema format '$name' already registered\n"
|
||||||
if $format_list->{$format};
|
if $format_list->{$name};
|
||||||
|
|
||||||
$format_list->{$format} = $code;
|
if ($validator) {
|
||||||
|
die "A \$validator function can only be specified for hash-based formats\n"
|
||||||
|
if ref($format) ne 'HASH';
|
||||||
|
$format_validators->{$name} = $validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
$format_list->{$name} = $format;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_format {
|
sub get_format {
|
||||||
my ($format) = @_;
|
my ($name) = @_;
|
||||||
return $format_list->{$format};
|
return $format_list->{$name};
|
||||||
}
|
}
|
||||||
|
|
||||||
my $renderer_hash = {};
|
my $renderer_hash = {};
|
||||||
@ -666,41 +673,49 @@ sub pve_verify_tfa_secret {
|
|||||||
sub check_format {
|
sub check_format {
|
||||||
my ($format, $value, $path) = @_;
|
my ($format, $value, $path) = @_;
|
||||||
|
|
||||||
return parse_property_string($format, $value, $path) if ref($format) eq 'HASH';
|
if (ref($format) eq 'HASH') {
|
||||||
|
# hash ref cannot have validator/list/opt handling attached
|
||||||
|
return parse_property_string($format, $value, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ref($format) eq 'CODE') {
|
||||||
|
# we are the (sole, old-style) validator
|
||||||
|
return $format->($value);
|
||||||
|
}
|
||||||
|
|
||||||
return if $format eq 'regex';
|
return if $format eq 'regex';
|
||||||
|
|
||||||
if ($format =~ m/^(.*)-a?list$/) {
|
my $parsed;
|
||||||
|
$format =~ m/^(.*?)(?:-a?(list|opt))?$/;
|
||||||
|
my ($format_name, $format_type) = ($1, $2 // 'none');
|
||||||
|
my $registered = get_format($format_name);
|
||||||
|
die "undefined format '$format'\n" if !$registered;
|
||||||
|
|
||||||
my $code = $format_list->{$1};
|
die "'-$format_type' format must have code ref, not hash\n"
|
||||||
|
if $format_type ne 'none' && ref($registered) ne 'CODE';
|
||||||
die "undefined format '$format'\n" if !$code;
|
|
||||||
|
|
||||||
|
if ($format_type eq 'list') {
|
||||||
# Note: we allow empty lists
|
# Note: we allow empty lists
|
||||||
foreach my $v (split_list($value)) {
|
foreach my $v (split_list($value)) {
|
||||||
&$code($v);
|
$parsed = $registered->($v);
|
||||||
}
|
}
|
||||||
|
} elsif ($format_type eq 'opt') {
|
||||||
} elsif ($format =~ m/^(.*)-opt$/) {
|
$parsed = $registered->($value) if $value;
|
||||||
|
|
||||||
my $code = $format_list->{$1};
|
|
||||||
|
|
||||||
die "undefined format '$format'\n" if !$code;
|
|
||||||
|
|
||||||
return if !$value; # allow empty string
|
|
||||||
|
|
||||||
&$code($value);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (ref($registered) eq 'HASH') {
|
||||||
my $code = $format_list->{$format};
|
# Note: this is the only case where a validator function could be
|
||||||
|
# attached, hence it's safe to handle that in parse_property_string.
|
||||||
die "undefined format '$format'\n" if !$code;
|
# We do however have to call it with $format_name instead of
|
||||||
|
# $registered, so it knows about the name (and thus any validators).
|
||||||
return parse_property_string($code, $value, $path) if ref($code) eq 'HASH';
|
$parsed = parse_property_string($format, $value, $path);
|
||||||
&$code($value);
|
} else {
|
||||||
|
$parsed = $registered->($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $parsed;
|
||||||
|
}
|
||||||
|
|
||||||
sub parse_size {
|
sub parse_size {
|
||||||
my ($value) = @_;
|
my ($value) = @_;
|
||||||
|
|
||||||
@ -754,9 +769,16 @@ sub parse_property_string {
|
|||||||
$additional_properties = 0 if !defined($additional_properties);
|
$additional_properties = 0 if !defined($additional_properties);
|
||||||
|
|
||||||
# Support named formats here, too:
|
# Support named formats here, too:
|
||||||
|
my $validator;
|
||||||
if (!ref($format)) {
|
if (!ref($format)) {
|
||||||
if (my $desc = $format_list->{$format}) {
|
if (my $reg = get_format($format)) {
|
||||||
$format = $desc;
|
die "parse_property_string only accepts hash based named formats\n"
|
||||||
|
if ref($reg) ne 'HASH';
|
||||||
|
|
||||||
|
# named formats can have validators attached
|
||||||
|
$validator = $format_validators->{$format};
|
||||||
|
|
||||||
|
$format = $reg;
|
||||||
} else {
|
} else {
|
||||||
die "unknown format: $format\n";
|
die "unknown format: $format\n";
|
||||||
}
|
}
|
||||||
@ -812,6 +834,7 @@ sub parse_property_string {
|
|||||||
raise "format error\n", errors => $errors;
|
raise "format error\n", errors => $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $validator->($res) if $validator;
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user