Amazon-API/APIExample.pm
package APIExample;
# wrapper for examples
use strict;
use warnings;
use Carp;
use English qw(-no_match_vars);
use Data::Dumper;
use JSON;
use Readonly;
our $VERSION = '0.03';
Readonly::Scalar our $TRUE => 1;
Readonly::Scalar our $FALSE => 0;
use parent qw(Exporter);
our @EXPORT = qw(dump_json normalize_options slurp slurp_json);
our @EXPORT_OK = qw($TRUE $FALSE);
our %EXPORT_TAGS = ( booleans => [qw($TRUE $FALSE)] );
########################################################################
sub slurp_json {
########################################################################
my ($file) = @_;
my $json = eval { return JSON::PP->new->decode( slurp($file) ) };
croak "ERROR: could not decode JSON string:\n$EVAL_ERROR\n"
if !$json || $EVAL_ERROR;
return $json;
}
########################################################################
sub slurp {
########################################################################
my ($file) = @_;
local $RS = undef;
open my $fh, '<', $file
or croak "ERROR: could not open $file\n";
my $content = <$fh>;
close $fh
or carp "ERROR: could not close $file\n";
return $content;
}
########################################################################
sub normalize_options {
########################################################################
my ($options) = @_;
foreach my $k ( keys %{$options} ) {
next if $k !~ /\-/xsm;
my $val = delete $options->{$k};
$k =~ s/\-/_/gxsm;
$options->{$k} = $val;
}
return %{$options};
}
########################################################################
sub dump_json {
########################################################################
my ($obj) = @_;
return JSON->new->pretty->encode($obj);
}
########################################################################
sub get_descriptions {
########################################################################
my ($package) = @_;
## no critic (ProhibitStringyEval, RequireInterpolationOfMetachars)
return eval '${' . $package . '::DESCRIPTIONS}';
}
########################################################################
sub version {
########################################################################
my ($package) = @_;
return eval "\$${package}::VERSION"; ## no critic (ProhibitStringyEval)
}
########################################################################
sub help {
########################################################################
my ( $package, $example, %options ) = @_;
my $descriptions = $package->get_descriptions;
if ($example) {
$descriptions = { $example, $descriptions->{$example} };
}
my $token;
if ( $options{pager} ) {
$token = eval {
require IO::Pager;
IO::Pager::open( *STDOUT, '|-:utf8', 'Unbuffered' );
};
}
print {*STDOUT} <<'HELP';
usage: perl service-name.pm options run command arguments
Options
-------
-h, --help this
-u, --endpoint-url alternate endpoint for AWS services
-p, --pager, --no-pager use, do not use a pager, default: use pager
Commands
--------
See below
HELP
foreach my $example ( keys %{$descriptions} ) {
print {*STDOUT} sprintf "%s : %s => %s\n", $package, $example, $descriptions->{$example};
}
exit;
}
########################################################################
sub service { goto &create_api }
########################################################################
sub create_api {
########################################################################
my ( $package, $options ) = @_;
return $package->new( { log_level => $options->{'log-level'}, url => $options->{'endpoint-url'} } );
}
########################################################################
sub get_options {
########################################################################
my ( $package, @opt_list ) = @_;
use Getopt::Long qw(:config no_ignore_case);
my %options = ( pager => 1, 'log-level' => 'info', );
my @option_specs = qw(
help|h
pager|p!
log-level|l=s
endpoint-url|u=s
version
);
GetOptions( \%options, @option_specs, @opt_list );
if ( $options{help} ) {
my $example = @ARGV;
$package->help( $example, %options );
}
if ( $options{version} ) {
print {*STDOUT} sprintf "%s\n", $package->version();
exit 0;
}
return \%options;
}
########################################################################
sub run {
########################################################################
my ( $package, $example, $options, @args ) = @_;
my $descriptions = $package->get_descriptions;
my $func = sprintf '_%s', $example;
croak 'no such command ' . $example
if !$descriptions->{$example} || !$package->can($func);
if ( $options->{'log-level'} eq 'trace' ) {
print {*STDERR} Dumper(
[ package => $package,
options => $options,
args => \@args
]
);
}
return $package->can($func)->( $package, $options, @args );
}
########################################################################
sub main {
########################################################################
my ($package) = @_;
use Carp::Always;
my $options = $package->get_options;
my $command = shift @ARGV;
my $method = shift @ARGV;
if ( $command eq 'run' ) {
$package->run( $method, $options, @ARGV );
}
exit 0;
}
1;
__END__
=pod
=head1 NAME
APIExample - Class for exercising various AWS APIs
=head1 SYNOPSIS
package Amazon::EC2;
use parent qw( Amazon::API::EC2 APIExample );
our $DESCRIPTIONS = {
DescribeSecurityGroups =>
'Executes the EC2 API "DescribeSubnets": run DescribeSubnets',
};
caller or __PACKAGE__->main;
########################################################################
sub _DescribeSecurityGroups {
########################################################################
my ( $package, $options, @args ) = @_;
my $ec2 = $package->new( url => $options->{'endpoint-url'} );
my @filter = param_n(
{ Filter => [
{ Name => 'group-name',
Value => ['tb*']
}
]
}
);
return print {*STDOUT} Dumper( $ec2->DescribeSecurityGroups( \@filter ) );
}
...
=head1 DESCRIPTION
This class acts as a wrapper for various classes that exercise a
subset of AWS API calls using the Amazon::API framework.
The API class are usually named for the service (e.g. C<ec2.pm>). Each
of those classes will use this class a parent class and provide
wrapper methods that correspond to the actual API method called by the
Amazon::API::{service} class. These wrapper classes should begin with
an underscore ('_') and essentially provide (or pass along the command
line arguments) the parameters to the method being tested.
The wrapper class usually looks something like:
sub _SomeMethod {
my ($package, $options, @args) = @_;
my $service = $package->new;
my $parameters = { SomeParmeter => 'some value' };
return print {*STDOUT} Dumper($service->SomeMethod($parameters));
}
=head1 METHODS AND SUBROUTINES
=head1 AUTHOR
Rob Lauer - <rlauer6@comcast.net>
=head1 SEE ALSO
L<Amazon::API>
=cut