#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use NGCP::Schema; use NGCP::Panel::Utils::Subscriber; use NGCP::Panel::Utils::Preferences; use Test::More; use Data::HAL qw(); use Data::HAL::Link qw(); use Safe::Isa qw($_isa); use NGCP::Panel::Form::CFSimpleAPI; my $schema = NGCP::Schema->connect(); my $ql_exists = 0; my ($ql,$ana); if($ql_exists){ #use DBIx::Class::QueryLog; #use DBIx::Class::QueryLog::Analyzer; my $ql = DBIx::Class::QueryLog->new; $schema->storage->debugobj($ql); $schema->storage->debug(1); my $ana = DBIx::Class::QueryLog::Analyzer->new({ querylog => $ql }); $ql->bucket('origin'); } my $time = time(); print "start;\n"; #print Dumper($schema); my $item_rs = $schema->resultset('voip_subscribers')->search( { 'me.status' => { '!=' => 'terminated' } },{ prefetch => { 'provisioning_voip_subscriber'=>'voip_cf_mappings'}, #prefetch => 'provisioning_voip_subscriber', rows => 200, }, ); my (@arr_orig,@arr_opt); for my $item ($item_rs->all) { # print Dumper({$item->get_inflated_columns}); my %resource = (); my $prov_subs = $item->provisioning_voip_subscriber; for my $cf_type (qw/cfu cfb cft cfna cfs/) { my $mapping = $schema->resultset('voip_cf_mappings')->search({ subscriber_id => $prov_subs->id, type => $cf_type, })->first; if ($mapping) { $resource{$cf_type} = _contents_from_cfm($mapping, $item); } else { $resource{$cf_type} = {}; } } if(keys %{$resource{cft}}){ my $ringtimeout_preference = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( c => undef, attribute => 'ringtimeout', prov_subscriber => $prov_subs, schema => $schema )->first; $ringtimeout_preference = $ringtimeout_preference ? $ringtimeout_preference->value : undef; $resource{cft}{ringtimeout} = $ringtimeout_preference; } additional_processing($item, \%resource); push @arr_orig, \%resource; } print "1.time=".(time()-$time).";\n"; #exit; if($ql_exists){ $ql->bucket('optimized'); } $time = time(); for my $item ($item_rs->all) { my %resource = (); my $prov_subs = $item->provisioning_voip_subscriber; @resource{qw/cfu cfb cft cfna cfs/} = ({}) x 5; for my $item_cf ($item->provisioning_voip_subscriber->voip_cf_mappings->all){ $resource{$item_cf->type} = _contents_from_cfm($item_cf, $item); } if(keys %{$resource{cft}}){ my $ringtimeout_preference = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( c => undef, attribute => 'ringtimeout', prov_subscriber => $prov_subs, schema => $schema )->first; $ringtimeout_preference = $ringtimeout_preference ? $ringtimeout_preference->value : undef; $resource{cft}{ringtimeout} = $ringtimeout_preference; } additional_processing($item, \%resource); push @arr_opt, \%resource; } print "2.time=".(time()-$time).";\n"; is_deeply(\@arr_orig, \@arr_opt, "check that arrays are equiv"); #print Dumper[\@arr_orig, \@arr_opt]; if($ql_exists){ print Dumper $ana->get_totaled_queries_by_bucket; } sub additional_processing{ my($item,$resource) = @_; my $type=''; my %resource=%$resource; my $hal = Data::HAL->new( links => [ Data::HAL::Link->new( relation => 'curies', href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}', name => 'ngcp', templated => 1, ), Data::HAL::Link->new(relation => 'collection', href => sprintf("%s", '')), Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'), Data::HAL::Link->new(relation => 'self', href => sprintf("%s%s", '', $item->id)), Data::HAL::Link->new(relation => "ngcp:$type", href => sprintf("/api/%s/%s", $type, $item->id)), Data::HAL::Link->new(relation => 'ngcp:subscribers', href => sprintf("/api/subscribers/%d", $item->id)), ], relation => 'ngcp:callforwards', ); my $form=NGCP::Panel::Form::CFSimpleAPI->new(); validate_form( form => $form, resource => \%resource, run => 0, ); $hal->resource(\%resource); } sub _contents_from_cfm { my ($cfm_item, $sub) = @_; my (@times, @destinations); my $timeset_item = $cfm_item->time_set; my $dset_item = $cfm_item->destination_set; for my $time ($timeset_item ? $timeset_item->voip_cf_periods->all : () ) { push @times, {$time->get_inflated_columns}; delete @{$times[-1]}{'time_set_id', 'id'}; } for my $dest ($dset_item ? $dset_item->voip_cf_destinations->all : () ) { my ($d, $duri) = NGCP::Panel::Utils::Subscriber::destination_to_field($dest->destination); my $deflated; if($d eq "uri") { $deflated = NGCP::Panel::Utils::Subscriber::uri_deflate(undef, $duri,$sub) if $d eq "uri"; $d = $dest->destination; } push @destinations, {$dest->get_inflated_columns, destination => $d, $deflated ? (simple_destination => $deflated) : (), }; delete @{$destinations[-1]}{'destination_set_id', 'id'}; } return {times => \@times, destinations => \@destinations}; } sub validate_form { my (%params) = @_; my $resource = $params{resource}; my $form = $params{form}; my $run = $params{run} // 1; my $exceptions = $params{exceptions} // []; my $form_params = $params{form_params} // {}; push @{ $exceptions }, "external_id"; my @normalized = (); # move {xxx_id} into {xxx}{id} for FormHandler foreach my $key(keys %{ $resource } ) { my $skip_normalize = grep {/^$key$/} @{ $exceptions }; if($key =~ /^(.+)_id$/ && !$skip_normalize && !exists $resource->{$1}) { push @normalized, $1; $resource->{$1}{id} = delete $resource->{$key}; } } # remove unknown keys my %fields = map { $_->name => $_ } $form->fields; validate_fields($resource, \%fields, $run); if($run) { # check keys/vals $form->process(params => $resource, posted => 1, %{$form_params} ); unless($form->validated) { my $e = join '; ', map { sprintf 'field=\'%s\', input=\'%s\', errors=\'%s\'', ($_->parent->$_isa('HTML::FormHandler::Field') ? $_->parent->name . '_' : '') . $_->name, $_->input // '', join('', @{ $_->errors }) } $form->error_fields; return; } } # move {xxx}{id} back into {xxx_id} for DB foreach my $key(@normalized) { next unless(exists $resource->{$key}); $resource->{$key . '_id'} = defined($resource->{$key}{id}) ? int($resource->{$key}{id}) : $resource->{$key}{id}; delete $resource->{$key}; } return 1; } sub validate_fields { my ($resource, $fields, $run) = @_; for my $k (keys %{ $resource }) { #if($resource->{$k}->$_isa('JSON::XS::Boolean') || $resource->{$k}->$_isa('JSON::PP::Boolean')) { if($resource->{$k}->$_isa('JSON::PP::Boolean')) { $resource->{$k} = $resource->{$k} ? 1 : 0; } unless(exists $fields->{$k}) { delete $resource->{$k}; } $resource->{$k} = DateTime::Format::RFC3339->format_datetime($resource->{$k}) if $resource->{$k}->$_isa('DateTime'); $resource->{$k} = $resource->{$k} + 0 if(defined $resource->{$k} && ( $fields->{$k}->$_isa('HTML::FormHandler::Field::Integer') || $fields->{$k}->$_isa('HTML::FormHandler::Field::Money') || $fields->{$k}->$_isa('HTML::FormHandler::Field::Float')) && (is_int($resource->{$k}) || is_decimal($resource->{$k}))); if (defined $resource->{$k} && $fields->{$k}->$_isa('HTML::FormHandler::Field::Repeatable') && "ARRAY" eq ref $resource->{$k} ) { for my $elem (@{ $resource->{$k} }) { my ($subfield_instance) = $fields->{$k}->fields; my %subfields = map { $_->name => $_ } $subfield_instance->fields; validate_fields($elem, \%subfields, $run); } } # only do this for converting back from obj to hal # otherwise it breaks db fields with the \0 and \1 notation unless($run) { $resource->{$k} = $resource->{$k} ? JSON::true : JSON::false if(defined $resource->{$k} && $fields->{$k}->$_isa('HTML::FormHandler::Field::Boolean')); } } return 1; } 1;