178 lines
5 KiB
Perl
178 lines
5 KiB
Perl
package Pear::LocalLoop::Plugin::Validators;
|
|
use Mojo::Base 'Mojolicious::Plugin';
|
|
|
|
use Email::Valid;
|
|
use Geo::UK::Postcode::Regex qw/ is_valid_pc /;
|
|
use Scalar::Util qw/ looks_like_number /;
|
|
use File::Basename qw/ fileparse /;
|
|
use DateTime::Format::Strptime;
|
|
use Try::Tiny;
|
|
|
|
sub register {
|
|
my ( $plugin, $app, $conf ) = @_;
|
|
|
|
$app->validator->add_check( email => sub {
|
|
my ( $validation, $name, $email ) = @_;
|
|
return Email::Valid->address( $email ) ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( in_resultset => sub {
|
|
my ( $validation, $name, $value, $key, $rs ) = @_;
|
|
return $rs->search({ $key => $value })->count ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( not_in_resultset => sub {
|
|
my ( $validation, $name, $value, $key, $rs ) = @_;
|
|
return $rs->search({ $key => $value })->count ? 1 : undef;
|
|
});
|
|
|
|
$app->validator->add_check( postcode => sub {
|
|
my ( $validation, $name, $value ) = @_;
|
|
return is_valid_pc( $value ) ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( number => sub {
|
|
my ( $validation, $name, $value ) = @_;
|
|
return looks_like_number( $value ) ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( gt_num => sub {
|
|
my ( $validation, $name, $value, $check ) = @_;
|
|
return $value > $check ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( lt_num => sub {
|
|
my ( $validation, $name, $value, $check ) = @_;
|
|
return $value < $check ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( filetype => sub {
|
|
my ( $validation, $name, $value, $filetype ) = @_;
|
|
my ( undef, undef, $extension ) = fileparse $value->filename, qr/\.[^.]*/;
|
|
$extension =~ s/^\.//;
|
|
return $app->types->type($extension) eq $filetype ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( is_iso_date => sub {
|
|
my ( $validation, $name, $value ) = @_;
|
|
$value = $app->iso_date_parser->parse_datetime( $value );
|
|
return defined $value ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( is_full_iso_datetime => sub {
|
|
my ( $validation, $name, $value ) = @_;
|
|
$value = $app->parse_iso_datetime( $value );
|
|
return defined $value ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( is_object => sub {
|
|
my ( $validation, $name, $value ) = @_;
|
|
return ref ( $value ) eq 'HASH' ? undef : 1;
|
|
});
|
|
|
|
$app->validator->add_check( in_range => sub {
|
|
my ( $validation, $name, $value, $low, $high ) = @_;
|
|
return $low < $value && $value < $high ? undef : 1;
|
|
});
|
|
|
|
$app->helper( validation_error => sub { _validation_error(@_) } );
|
|
}
|
|
|
|
=head2 validation_error
|
|
|
|
Returns undef if there is no validation error, returns true otherwise - having
|
|
set the errors up as required. Renders out the errors as an array, with status
|
|
400
|
|
|
|
=cut
|
|
|
|
sub _validation_error {
|
|
my ( $c, $sub_name ) = @_;
|
|
|
|
my $val_data = $c->validation_data->{ $sub_name };
|
|
return unless defined $val_data;
|
|
my $data = $c->stash->{api_json};
|
|
|
|
my @errors = _validate_set( $c, $val_data, $data );
|
|
|
|
if ( scalar @errors ) {
|
|
my @sorted_errors = sort @errors;
|
|
$c->render(
|
|
json => {
|
|
success => Mojo::JSON->false,
|
|
errors => \@sorted_errors,
|
|
},
|
|
status => 400,
|
|
);
|
|
return \@errors;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub _validate_set {
|
|
my ( $c, $val_data, $data, $parent_name ) = @_;
|
|
|
|
my @errors;
|
|
|
|
# MUST get a raw validation object
|
|
my $validation = $c->app->validator->validation;
|
|
$validation->input( $data );
|
|
|
|
for my $val_data_key ( keys %$val_data ) {
|
|
|
|
$validation->topic( $val_data_key );
|
|
|
|
my $val_set = $val_data->{$val_data_key};
|
|
|
|
my $custom_check_prefix = {};
|
|
|
|
for my $val_error ( @{$val_set->{validation}} ) {
|
|
my ( $val_validator ) = keys %$val_error;
|
|
|
|
unless (
|
|
$validation->validator->checks->{$val_validator}
|
|
|| $val_validator =~ /required|optional/
|
|
) {
|
|
$c->app->log->warn( 'Unknown Validator [' . $val_validator . ']' );
|
|
next;
|
|
}
|
|
|
|
if ( my $custom_prefix = $val_error->{ $val_validator }->{ error_prefix } ) {
|
|
$custom_check_prefix->{ $val_validator } = $custom_prefix;
|
|
}
|
|
my $val_args = $val_error->{ $val_validator }->{ args };
|
|
|
|
$validation->$val_validator(
|
|
( $val_validator =~ /required|optional/ ? $val_data_key : () ),
|
|
( defined $val_args ? @$val_args : () )
|
|
);
|
|
|
|
# stop bothering checking if failed, validation stops after first failure
|
|
last if $validation->has_error( $val_data_key );
|
|
}
|
|
|
|
if ( $validation->has_error( $val_data_key ) ) {
|
|
my ( $check ) = @{ $validation->error( $val_data_key ) };
|
|
my $error_prefix = defined $custom_check_prefix->{ $check }
|
|
? $custom_check_prefix->{ $check }
|
|
: $check;
|
|
my $error_string = join ('_',
|
|
$error_prefix,
|
|
( defined $parent_name ? $parent_name : () ),
|
|
$val_data_key,
|
|
);
|
|
push @errors, $error_string;
|
|
} elsif ( defined $val_set->{ children } ) {
|
|
push @errors, _validate_set(
|
|
$c,
|
|
$val_set->{ children },
|
|
$data->{ $val_data_key },
|
|
$val_data_key );
|
|
}
|
|
}
|
|
|
|
return @errors;
|
|
}
|
|
|
|
1;
|