2017-04-08 17:25:34 +00:00
|
|
|
package Pear::LocalLoop::Plugin::Validators;
|
|
|
|
use Mojo::Base 'Mojolicious::Plugin';
|
|
|
|
|
|
|
|
use Email::Valid;
|
2017-07-25 16:07:15 +00:00
|
|
|
use Geo::UK::Postcode::Regex qw/ is_valid_pc /;
|
2017-04-20 00:27:18 +00:00
|
|
|
use Scalar::Util qw/ looks_like_number /;
|
2017-04-23 16:23:35 +00:00
|
|
|
use File::Basename qw/ fileparse /;
|
2017-04-23 15:59:35 +00:00
|
|
|
use DateTime::Format::Strptime;
|
2017-04-25 19:50:34 +00:00
|
|
|
use Try::Tiny;
|
2017-04-08 17:25:34 +00:00
|
|
|
|
|
|
|
sub register {
|
2021-03-20 12:09:50 +00:00
|
|
|
my ( $plugin, $app, $conf ) = @_;
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
email => sub {
|
|
|
|
my ( $validation, $name, $email ) = @_;
|
|
|
|
return Email::Valid->address($email) ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
in_resultset => sub {
|
|
|
|
my ( $validation, $name, $value, $key, $rs ) = @_;
|
|
|
|
return $rs->search( { $key => $value } )->count ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
not_in_resultset => sub {
|
|
|
|
my ( $validation, $name, $value, $key, $rs ) = @_;
|
|
|
|
return $rs->search( { $key => $value } )->count ? 1 : undef;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
postcode => sub {
|
|
|
|
my ( $validation, $name, $value ) = @_;
|
|
|
|
return is_valid_pc($value) ? undef : 1;
|
|
|
|
}
|
2017-09-27 17:01:06 +00:00
|
|
|
);
|
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
number => sub {
|
|
|
|
my ( $validation, $name, $value ) = @_;
|
|
|
|
return looks_like_number($value) ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
gt_num => sub {
|
|
|
|
my ( $validation, $name, $value, $check ) = @_;
|
|
|
|
return $value > $check ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
lt_num => sub {
|
|
|
|
my ( $validation, $name, $value, $check ) = @_;
|
|
|
|
return $value < $check ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
is_full_iso_datetime => sub {
|
|
|
|
my ( $validation, $name, $value ) = @_;
|
|
|
|
$value = $app->parse_iso_datetime($value);
|
|
|
|
return defined $value ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
is_object => sub {
|
|
|
|
my ( $validation, $name, $value ) = @_;
|
|
|
|
return ref($value) eq 'HASH' ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->validator->add_check(
|
|
|
|
in_range => sub {
|
|
|
|
my ( $validation, $name, $value, $low, $high ) = @_;
|
|
|
|
return $low < $value && $value < $high ? undef : 1;
|
|
|
|
}
|
|
|
|
);
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
$app->helper( validation_error => sub { _validation_error(@_) } );
|
2021-03-20 15:02:00 +00:00
|
|
|
|
|
|
|
return 1;
|
2021-03-20 12:09:50 +00:00
|
|
|
}
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
=head2 validation_error
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
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
|
2017-09-27 17:01:06 +00:00
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
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;
|
2017-09-27 17:01:06 +00:00
|
|
|
}
|
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
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 );
|
|
|
|
}
|
2017-09-27 17:01:06 +00:00
|
|
|
}
|
|
|
|
|
2021-03-20 12:09:50 +00:00
|
|
|
return @errors;
|
2017-04-08 17:25:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
1;
|