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;