#!/usr/bin/perl use strict; use warnings; use Anvil::Tools; use JSON; use Data::Dumper; $| = 1; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) { $running_directory =~ s/^\./$ENV{PWD}/; } my $anvil = Anvil::Tools->new(); sub is_array { return ref($_[0]) eq "ARRAY"; } sub is_hash { return ref($_[0]) eq "HASH"; } sub db_access { my $parameters = shift; my $db_access_mode = $parameters->{db_access_mode}; my $db_uuid = $parameters->{db_uuid}; my $sql_query = $parameters->{sql_query}; my $access_parameters = { query => $sql_query, uuid => $db_uuid, source => $THIS_FILE, line => __LINE__ }; return ($db_access_mode eq "write") ? { write_code => $anvil->Database->write($access_parameters) } : $anvil->Database->query($access_parameters); } sub call_pre_data_fns { my $parameters = shift; my $fns = $parameters->{fns}; if (is_array($fns)) { foreach my $fn_wrapper ( @{$fns} ) { if (is_array($fn_wrapper)) { my $pre_chain = @{$fn_wrapper}[0] // ""; my @fn_params = @{$fn_wrapper}[1..$#{$fn_wrapper}] // (); my @chain = split(/->|,/, $pre_chain); my $intermediate = $anvil; my $key_index = 0; foreach my $key ( @chain ) { last if not defined $intermediate->${key}; if ($key_index == $#chain && $intermediate->can($key)) { eval { $intermediate->${key}(@fn_params); }; } else { $intermediate = $intermediate->${key}; } $key_index += 1; } } } } } sub get_anvil_data { my $parameters = shift; my $chain = $parameters->{chain}; my $source_intermediate = $anvil->data; my $target_intermediate = $parameters->{data}; my $key_index = 0; foreach my $key ( @{$chain} ) { last if not exists $source_intermediate->{$key}; $source_intermediate = $source_intermediate->{$key}; if (not exists $target_intermediate->{$key}) { $target_intermediate->{$key} = {}; } if ($key_index < $#{$chain}) { $target_intermediate = $target_intermediate->{$key}; } else { $target_intermediate->{$key} = $source_intermediate; } $key_index += 1; } } sub call_fn { my $parameters = shift; my $chain = $parameters->{chain}; my $fallback = $parameters->{fallback}; my $fn_wrapper = $parameters->{fn}; if (exists $fn_wrapper->{fn}) { my $fn = $fn_wrapper->{fn}; my $fn_params = $fn_wrapper->{params}; $fn_params->{chain} = $chain; return $fn->($fn_params); } else { return $fallback; } } sub foreach_nested { my $parameters = shift; # Required parameters: my $hash = $parameters->{hash}; # Optional parameters: my $chain = exists $parameters->{chain} ? $parameters->{chain} : (); my $depth = exists $parameters->{depth} ? $parameters->{depth} : 0; my $on_key = exists $parameters->{on_key} ? $parameters->{on_key} : {}; my $on_chain_end = exists $parameters->{on_chain_end} ? $parameters->{on_chain_end} : {}; foreach my $key (keys %{$hash}) { my $is_continue_chain = 1; my $value = $hash->{$key}; push(@{$chain}, $key); $is_continue_chain = call_fn({ chain => $chain, fallback => $is_continue_chain, fn => $on_key }); if ( ($is_continue_chain) && is_hash($value) ) { foreach_nested({ chain => $chain, depth => $depth + 1, hash => $value, on_chain_end => $on_chain_end, on_key => $on_key, }); } else { call_fn({ chain => $chain, fn => $on_chain_end }); } pop(@{$chain}); } } $anvil->Get->switches; $anvil->Database->connect; $anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" }); if (not $anvil->data->{sys}{database}{connections}) { # No databases, exit. $anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003" }); $anvil->nice_exit({ exit_code => 1 }); } my $data_hash = $anvil->data->{switches}{'data'}; my $db_access_mode = defined $anvil->data->{switches}{'mode'} ? $anvil->data->{switches}{'mode'} : ""; my $db_uuid = $anvil->data->{switches}{'uuid'}; my $pre_data = $anvil->data->{switches}{'predata'}; my $sql_query = $anvil->data->{switches}{'query'}; my $sub_module_name = defined $anvil->data->{switches}{'sub-module'} ? $anvil->data->{switches}{'sub-module'} : "Database"; my $sub_name = defined $anvil->data->{switches}{'sub'} ? $anvil->data->{switches}{'sub'} : ""; my $sub_params = defined $anvil->data->{switches}{'sub-params'} ? $anvil->data->{switches}{'sub-params'} : "{}"; if ($sql_query) { my $results = db_access({ db_uuid => $db_uuid, sql_query => $sql_query, db_access_mode => $db_access_mode }); print JSON->new->utf8->encode($results)."\n"; } elsif ($anvil->${sub_module_name}->can($sub_name)) { my $decoded_sub_params; my $is_decode_sub_params_success = eval { $decoded_sub_params = decode_json($sub_params); }; if (not $is_decode_sub_params_success) { print STDERR "error: failed to parse subroutine parameters\n"; $anvil->nice_exit({ exit_code => 1 }); } my (@results) = $anvil->${sub_module_name}->${sub_name}($decoded_sub_params); print JSON->new->utf8->encode({ sub_results => scalar(@results) > 1 ? \@results : $results[0] })."\n"; } elsif ($data_hash) { if ($pre_data) { my $decoded_pre_data; my $is_decode_pre_data_success = eval { $decoded_pre_data = decode_json($pre_data); }; if ($is_decode_pre_data_success && is_array($decoded_pre_data)) { call_pre_data_fns({ fns => $decoded_pre_data }); } } my $decoded_data_hash; my $is_decode_data_hash_success = eval { $decoded_data_hash = decode_json($data_hash); }; if (not $is_decode_data_hash_success) { print STDERR "error: failed to parse data structure\n"; $anvil->nice_exit({ exit_code => 1 }); } my $get_anvil_data_params = { data => {} }; foreach_nested({ hash => $decoded_data_hash, on_chain_end => { fn => \&get_anvil_data, params => $get_anvil_data_params }, }); print JSON->new->utf8->allow_blessed->encode($get_anvil_data_params->{data})."\n"; } else { print STDERR "error: missing switches and perhaps their respective parameters; one of --data, --query, or --sub is required\n"; $anvil->nice_exit({ exit_code => 1 }); } $anvil->nice_exit({ exit_code => 0 });