Amazon-API/lib/Amazon/API/Botocore.pm
#!/usr/bin/env perl
package Amazon::API::Botocore;
use strict;
use warnings;
BEGIN {
use English qw(-no_match_vars);
eval { require Log::Log4perl; };
if ($EVAL_ERROR) {
no strict qw(refs); ## no critic (ProhibitNoStrict)
for (qw(DEBUG INFO WARN ERROR FATAL)) {
*{ __PACKAGE__ . "::$_" } = sub { };
}
}
else {
no warnings; ## no critic (ProhibitNoWarnings)
require Log::Log4perl::Level;
Log::Log4perl::Level->import(__PACKAGE__);
Log::Log4perl->import(qw(:easy));
}
}
use parent qw( Exporter );
use Amazon::API::Botocore::Shape::Utils qw(require_class create_shape create_module_name);
use Amazon::API::Template qw(:all);
use Amazon::API::Pod::Parser qw(get_pod_section);
use Carp;
use Carp::Always;
use Cwd;
use Data::Dumper;
use English qw( -no_match_vars );
use File::Find;
use File::Path qw(make_path);
use Getopt::Long qw(:config no_ignore_case);
use JSON;
use List::MoreUtils qw( first_index );
use List::Util qw( max );
use Pod::Usage;
use Pod::Text;
use Readonly;
use charnames qw(:full);
# package constants
Readonly::Scalar our $BOTO_PATH_OFFSET => 3;
Readonly::Scalar our $SHAPE_FILE_TEMPLATE => '%sAmazon/API/Botocore/Shape/%s/';
Readonly::Scalar our $REQUEST_CLASS_TEMPLATE => 'Amazon::API::Botocore::Shape::%s::%sRequest';
Readonly::Scalar our $STRINGIFY => 'Dumper';
use Amazon::API::Constants qw( :all );
use Amazon::API::Botocore::Pod qw( pod );
our $VERSION = '2.1.4'; ## no critic (RequireInterpolationOfMetachars)
our %BOTO_SERVICES;
our $TEMPLATE_START = tell DATA;
our @EXPORT_OK = qw(
%BOTO_SERVICES
create_module_name
create_service_shapes
fetch_boto_services
get_service_descriptions
paginator
);
our %EXPORT_TAGS = (
all => [
qw(
create_service_shapes
fetch_boto_services
get_service_descriptions
%BOTO_SERVICES)
],
);
caller or __PACKAGE__->main;
########################################################################
sub paginator {
########################################################################
my (%options) = @_;
my ( $service, $api, $parameters ) = @options{qw(service api parameters)};
$parameters //= {};
croak "no service\n"
if !$service && ref($service) =~ /^Amazon::API::/xsm;
croak "no api name\n"
if !$api;
my $service_name = ref $service;
if ( $service_name =~ /^Amazon::API::([^:]+)$/xsm ) {
$service_name = $1;
}
else {
croak "could not determine service name\n";
}
my $request_class = sprintf $REQUEST_CLASS_TEMPLATE, $service_name, $api;
my $ret = eval { require_class $request_class; };
croak "unable to find a request class: $request_class\n"
if !$ret || $EVAL_ERROR;
my $paginator = $service->get_paginators->{$api};
my $input_token = $paginator->{input_token};
my $more_results = $paginator->{more_results};
my $limit_key = $paginator->{limit_key};
my $output_token = $paginator->{output_token};
my $result_key = $paginator->{result_key};
my $request = $request_class->new($parameters);
my @response;
$more_results //= $output_token;
while ( my $rsp = $service->$api($request) ) {
push @response, @{ $rsp->{$result_key} };
last if !$rsp->{$more_results};
my $limit = $rsp->{$limit_key};
$request = $request_class->new(
{ $limit ? ( $limit_key => $limit ) : (), $input_token => $rsp->{$output_token} } );
}
return \@response;
}
########################################################################
sub parse_request_uri {
########################################################################
my ($uri) = @_;
my ( $path, $query_string ) = split /\N{QUESTION MARK}/xsm, $uri;
my @path_parts = split /\N{SOLIDUS}/xsm, $path;
my @path_args;
foreach my $idx ( 1 .. $#path_parts + 1 ) {
if ( $path_parts[$idx] && $path_parts[$idx] =~ /^[{](.*)[}]$/xsm ) {
push @path_args, ucfirst $1;
$path_parts[$idx] = '%s';
}
}
my $request_uri = join $SLASH, @path_parts;
if ($query_string) {
$request_uri .= $QUESTION_MARK . $query_string;
}
return ( $request_uri || $uri, \@path_args );
}
########################################################################
sub fetch_json_file {
########################################################################
my ($path) = @_;
my $json;
open my $fh, '<', $path
or croak 'could not open ' . $path;
{
local $RS = undef;
$json = JSON->new->utf8->decode(<$fh>);
}
close $fh
or croak 'could not close ' . $path;
return $json;
}
########################################################################
sub fetch_service_description { goto &fetch_json_file; }
sub fetch_paginators { goto &fetch_json_file; }
########################################################################
########################################################################
sub create_service_shapes {
########################################################################
my (%options) = @_;
my ( $service, $boto_path, $shape_path, $module_name )
= @options{qw(service botocore-path output-path module-name)};
fetch_boto_services($boto_path);
my $service_description
= get_service_descriptions($service)->[0]->{$service};
if ( $shape_path && $shape_path !~ /\/\z/xsm ) {
$shape_path = "$shape_path/";
$shape_path = sprintf $SHAPE_FILE_TEMPLATE, $shape_path, $module_name;
if ( !-d $shape_path ) {
croak "could not create $shape_path"
if !make_path $shape_path;
}
}
my $shapes = $service_description->{shapes};
my $count = 0;
foreach my $shape_name ( keys %{$shapes} ) {
my $class = create_shape(
name => $shape_name,
service_description => $service_description,
service => $module_name
);
if ($shape_path) {
my $module = sprintf '%s%s.pm', $shape_path, $shape_name;
open my $fh, '>', $module
or croak "could not open $module for writing";
binmode $fh, 'encoding(UTF-8)';
print {$fh} $class;
close $fh;
}
else {
print {*STDOUT} $class;
}
$count++;
}
return $count;
}
# File::Find callback - collect paths to most recent service-2.json
# files for all AWS services
########################################################################
sub find_latest_services {
########################################################################
my $file = $_;
my $dir = $File::Find::name;
return if $dir !~ qr/botocore\N{SOLIDUS}botocore/xsm;
return if $file !~ qr/service\N{HYPHEN-MINUS}2\N{FULL STOP}json/xsm;
my (@path) = split /\N{SOLIDUS}/xsm, $dir;
my $boto_path = first_index {/botocore/xsm} @path;
# this should not happen...
if ( $boto_path < 0 ) {
croak 'no botocore in path ' . $dir;
}
$boto_path += $BOTO_PATH_OFFSET;
my ( $service, $date ) = @path[ $boto_path, $boto_path + 1, ];
$BOTO_SERVICES{$service}->{date} = $BOTO_SERVICES{$service}->{date} // $EMPTY;
if ( $date gt $BOTO_SERVICES{$service}->{date} ) {
$BOTO_SERVICES{$service} = {
date => $date,
path => \@path
};
}
return $file;
}
########################################################################
sub render_stub {
########################################################################
my (%args) = @_;
my %options = %{ $args{options} };
my $service = $args{service};
my $template = $args{template};
my $parameters = $args{parameters};
my $operations = $parameters->{operations};
my $shapes = $parameters->{shapes};
my $metadata = $parameters->{metadata};
my $paginators = $parameters->{paginators};
if ( $parameters->{paginators} ) {
$paginators = $paginators->{pagination};
}
$parameters->{ to_template_var('program_name') } = $PROGRAM_NAME;
$parameters->{ to_template_var('program_version') } = $VERSION;
$parameters->{ to_template_var('timestamp') } = scalar localtime;
$parameters->{ to_template_var('end') } = '__END__';
$parameters->{ to_template_var('description') }
= $metadata->{serviceFullName};
$parameters->{ to_template_var('metadata') }
= stringify( $parameters->{metadata} );
my %methods;
my @errors;
foreach my $m ( keys %{$operations} ) {
my %operation = %{ $operations->{$m} };
my $documentation;
if ( $options{pod} ) {
$documentation = html2pod( $operation{documentation} // $EMPTY );
}
$methods{$m} = {
documentation => $documentation,
input => $operation{input}->{shape},
output => $operation{output}->{shape},
http => $operation{http},
errors => [ map { $_->{shape} } @{ $operation{errors} } ],
};
delete $operations->{$m}->{documentation};
}
my @pod;
foreach my $method ( sort keys %methods ) {
my $input = $methods{$method}->{input} // $EMPTY;
my $output = $methods{$method}->{output} // $EMPTY;
my $errors = $methods{$method}->{errors} // $EMPTY;
if ( $options{pod} ) {
my $documentation = $EMPTY;
$documentation = $methods{$method}->{documentation} // $EMPTY;
$documentation =~ s/\A\n+//xsm;
$documentation =~ s/\n+\z//xsm;
if ($input) {
my $input_shape = $shapes->{$input};
local $LIST_SEPARATOR = "\n\n";
my @items
= map { sprintf '=item %s', $_ } @{ $input_shape->{required} };
my $required = "@items\n";
my $members = $EMPTY;
my %shape_members = %{ $input_shape->{members} };
foreach my $m ( sort keys %shape_members ) {
my $location = $shape_members{$m}->{locationName} || $m;
$members .= sprintf "\n=item %s\n", $m;
$members .= html2pod $shape_members{$m}->{documentation};
}
$required ||= 'None';
$input = <<"INPUT";
=over 5
=item $input
=over 5
=item Parameters
=over 5
$members
=back
=item Required
=over 5
$required
=back
=back
=back
INPUT
}
if ($output) {
my $members = $EMPTY;
my $output_shape = $shapes->{$output};
my %shape_members = %{ $output_shape->{members} // {} };
foreach my $m ( sort keys %shape_members ) {
$members .= sprintf "\n=item %s\n", $m;
$members .= html2pod $shape_members{$m}->{documentation};
}
$output = <<"OUTPUT";
=over 5
=item $output
=over 5
=item Parameters
=over 5
$members
=back
=back
=back
OUTPUT
}
my @error_items;
if ( @{$errors} ) {
foreach my $e ( @{$errors} ) {
my $shape = $shapes->{$e};
push @error_items, '=over 5';
push @error_items, sprintf "=item %s\n%s", $e, html2pod $shape->{documentation};
if ( $shape->{error} ) {
push @error_items, '=over 5';
foreach my $k ( sort keys %{ $shape->{error} } ) {
push @error_items, sprintf "=item %s\n\n%s", $k, $shape->{error}->{$k};
}
push @error_items, "=back\n";
}
push @error_items, "=back\n";
}
local $LIST_SEPARATOR = "\n\n";
my $error_str = "@error_items";
$errors = <<"ERRORS";
$error_str
ERRORS
}
else {
$errors = $EMPTY;
}
my $none = "=over 5\n\n=item NONE\n\n=back\n\n";
$output = $output || $none;
$errors = $errors || $none;
$input = $input || $none;
my $method_pod = <<'END_OF_POD';
=head2 @method@
@documentation@
=over 5
=item INPUT
@input@
=item OUTPUT
@output@
=item ERRORS
@errors@
=back
END_OF_POD
my $http = $methods{$method}->{http};
my $http_method = $EMPTY;
my $request_uri = $EMPTY;
if ($http) {
$http_method = $http->{method} // $EMPTY;
$request_uri = $http->{requestUri} // $EMPTY;
my ( $request_uri_tpl, $args ) = parse_request_uri($request_uri);
$operations->{$method}->{http}->{parsed_request_uri}
= { request_uri_tpl => $request_uri_tpl, parameters => $args };
$method_pod .= <<'END_OF_POD';
=over 5
=item METHOD
@http_method@
=item REQUEST URI
@request_uri@
END_OF_POD
}
$method_pod .= <<'END_OF_POD';
=back
END_OF_POD
my @see_also;
push @see_also, $methods{$method}->{input} || ();
push @see_also, $methods{$method}->{output} || ();
push @see_also, map { $_->{shape} } @{ $operations->{$method}->{errors} };
$parameters->{ to_template_var('see_also') } = join "\n",
map { sprintf 'L<%s::%s>', $parameters->{ to_template_var('package_name') }, $_ } @see_also;
$parameters->{ to_template_var('method') } = $method;
$parameters->{ to_template_var('errors') } = $errors;
$parameters->{ to_template_var('input') } = $input;
$parameters->{ to_template_var('output') } = $output;
$parameters->{ to_template_var('http_method') } = $http_method;
$parameters->{ to_template_var('request_uri') } = $request_uri;
$parameters->{ to_template_var('documentation') } = $documentation;
my $package_name = $parameters->{ to_template_var('package_name') };
$package_name =~ s/::/\//gxsm;
push @pod, render_template $method_pod, $parameters;
my $pod_stub = <<'END_OF_POD';
=pod
=encoding utf8
=head1 NAME
@method@
=head1 SYNOPSIS
my $service = @package_name@->new;
my $rsp = $service->@method@($parameters);
=head1 DESCRIPTION
@documentation@
=head1 PARAMETERS
=head2 INPUT
@input@
=head2 OUTPUT
@output@
=head2 ERRORS
@errors@
=head1 NOTES
=over 5
=item * Method: @http_method@
=item * Request URI: @request_uri@
=back
=head1 SEE ALSO
@see_also@
=head1 AUTHOR
Autogenerate by @program_name@ on @timestamp@
=head1 LICENSE AND COPYRIGHT
This module is free software it may be used, redistributed and/or
modified under the same terms as Perl itself.
=cut
1;
END_OF_POD
# NOTE: this is JUST pod, so if you don't provide an output path,
# you won't get the method pod
if ( $options{'output-path'} ) {
my $method_pod_file = sprintf '%s/%s/%s.pm', $options{'output-path'}, $package_name, $method;
my $template = render_template( $pod_stub, $parameters );
if ( $options{'output-path'} ne $DASH ) {
open my $fh, '>', $method_pod_file
or croak "could not open $method_pod_file for writing";
print {$fh} $template;
close $fh;
}
else {
print {*STDOUT} $template;
}
}
}
$parameters->{ to_template_var('methods') } = join "\n", @pod;
}
$parameters->{ to_template_var('operations') } = stringify($operations);
$parameters->{ to_template_var('shapes') } = stringify($shapes);
$parameters->{ to_template_var('paginators') } = stringify($paginators);
if ( !$options{pod} ) {
$template =~ s/^\@end.*\z//xsm;
}
return render_template( $template, $parameters );
}
########################################################################
sub stringify {
########################################################################
my ($object) = @_;
return $object
if !ref $object;
local $Data::Dumper::Terse = $TRUE;
local $Data::Dumper::Deepcopy = $TRUE;
return Dumper($object)
if $STRINGIFY eq 'Dumper';
return JSON->new->utf8->encode($object);
}
########################################################################
sub get_api_descriptions {
########################################################################
goto &get_service_descriptions;
}
########################################################################
sub get_service_descriptions {
########################################################################
my @services = @_;
my @descriptions;
if ( !@services ) {
@services = sort keys %BOTO_SERVICES;
}
foreach my $s ( map {lc} @services ) {
croak "no such service: $s\n"
if !$BOTO_SERVICES{$s};
my @path = @{ $BOTO_SERVICES{$s}->{path} };
my $boto_path = first_index {/botocore/xsm} @path;
if ( $boto_path < 0 ) {
croak 'no botocore in path ' . $BOTO_SERVICES{$s}->{path};
}
my $service_path = join $SLASH, @path[ 0 .. $boto_path + 2 ], $s, $BOTO_SERVICES{$s}->{date};
my $service_file = "$service_path/service-2.json";
my $paginators_file = "$service_path/paginators-1.json";
my $paginators = eval { return fetch_paginators($paginators_file); };
my $service_description = fetch_service_description($service_file);
if ( $service_description->{operations} ) {
my $operations = $service_description->{operations};
my $shapes = $service_description->{shapes};
my $metadata = $service_description->{metadata};
my $service_name = $metadata->{signingName} || $metadata->{endpointPrefix};
push @descriptions,
{
$s => {
actions => [ keys %{$operations} ],
documentation => $service_description->{documentation},
endpoint_prefix => $metadata->{endpointPrefix},
json_version => $metadata->{jsonVersion},
metadata => $metadata,
metadata_keys => [ keys %{$metadata} ],
operations => $operations,
paginators => $paginators,
protocol => $metadata->{protocol},
service_name => $service_name,
shapes => $shapes,
target_prefix => $metadata->{targetPrefix},
version => $BOTO_SERVICES{$s}->{date},
}
};
}
}
return \@descriptions;
}
########################################################################
sub fetch_boto_services {
########################################################################
my ($path) = @_;
if ( !-d "$path/botocore" ) {
croak <<"END_OF_CROAKING";
!!! No $path/botocore directory found.
In order to create stubs or shapes you must clone the Botocore project
and provide the path to the project using the -b option or by setting
the environment variable BOTOCORE_PATH.
git clone https://github.com/boto/botocore.git /tmp/botocore
export BOTOCORE_PATH=/tmp/botocore
END_OF_CROAKING
}
find( { wanted => \&find_latest_services, follow => $TRUE }, $path );
if ( !keys %BOTO_SERVICES ) {
croak 'no services found in path ' . $path;
}
return keys %BOTO_SERVICES;
}
########################################################################
sub extra_args {
########################################################################
my (%options) = @_;
return shift @{ $options{'extra-args'} };
}
########################################################################
sub dump_service {
########################################################################
my (%options) = @_;
fetch_boto_services( $options{'botocore-path'} );
my $service = extra_args(%options) // $options{service};
croak 'no service specified'
if !$service;
my @services = $service eq 'all' ? keys %BOTO_SERVICES : $service;
my $description = get_api_descriptions(@services);
print JSON->new->pretty->encode( $description->[0] );
return $TRUE;
}
########################################################################
sub create_stub {
########################################################################
my (%options) = @_;
fetch_boto_services( $options{'botocore-path'} );
my ( $service, $module_name ) = @options{qw( service module-name)};
$service = lc $service;
my $package_name = sprintf 'Amazon::API::%s', $module_name;
croak 'no service specified'
if !$service;
my $description = get_api_descriptions($service);
my $parameters = $description->[0]->{$service};
$parameters->{package_name} = $package_name;
my @actions = @{ $parameters->{actions} };
$parameters->{actions} = $PADDING . join "\n ", sort @actions;
if ( $parameters->{protocol} eq 'rest-json' ) {
foreach (@actions) {
}
}
$parameters->{service}
= $parameters->{service_name} || $parameters->{endpoint_prefix};
if ( $parameters->{protocol} eq 'query' ) {
$parameters->{content_type} = 'application/x-www-form-urlencoded';
}
if ( $parameters->{protocol} eq 'json' ) {
$parameters->{content_type} = 'application/x-amz-json-' . $parameters->{json_version};
}
# for rest-json protocol we need a method and and a query uri in
# addition to the payload
if ( $parameters->{protocol} eq 'rest-json' ) {
$parameters->{content_type} = 'application/json';
}
my @template_vars = qw(
actions
botocore_metadata
botocore_operation
content_type
endpoint_prefix
package_name
protocol
service
target_prefix
version
);
foreach my $var (@template_vars) {
$parameters->{ to_template_var($var) } = $parameters->{$var};
}
my $module = render_stub(
service => $service,
template => fetch_template( *DATA, $TEMPLATE_START ),
parameters => $parameters,
options => \%options,
);
if ( $options{tidy} && eval { require Perl::Tidy; } ) {
print {*STDERR} "tidying module...this may take a while\n";
my $tidy_module = $EMPTY;
if (
Perl::Tidy::perltidy(
argv => [],
source => \$module,
destination => \$tidy_module,
)
) {
croak 'could not tidy module!';
}
$module = $tidy_module;
}
my $file = $options{file};
if ( !$file && $options{'output-path'} ) {
my $path = sprintf '%s/Amazon/API', $options{'output-path'};
if ( !-d $path ) {
croak "could not create $path"
if !make_path $path;
}
$file = sprintf '%s/%s.pm', $path, $module_name;
if ( -e $file ) {
rename $file, "$file.bak";
}
}
my $fh = eval {
if ($file) {
open my $handle, '>', $file
or croak 'could not open ' . $file;
return $handle;
}
else {
return *STDOUT;
}
};
print {$fh} $module;
close $fh
or croak 'could close file';
return $TRUE;
}
########################################################################
sub format_columns {
########################################################################
my (%args) = @_;
my $text = $args{text};
my $padding = $args{padding} // 2;
my $column_width = $args{'column-width'};
my $indent = $args{indent} ? $SPACE x $args{indent} : $EMPTY;
my $max_width = max map {length} @{$text};
my $width = $args{width}; # width of canvas
# format for screen by default
if ( !$width ) {
require Term::ReadKey;
Term::ReadKey->import('GetTerminalSize');
($width) = eval { GetTerminalSize() };
$width //= 80;
}
if ( !$column_width ) {
$column_width = 2 + $max_width;
}
my $columns = int $width / $column_width;
my @formatted_text = map { sprintf "%-${column_width}s", $_; } @{$text};
my $output = $EMPTY;
while (@formatted_text) {
$output .= sprintf "%s%s\n", $indent, join $EMPTY, grep {defined} @formatted_text[ ( 0 .. $columns - 1 ) ];
for ( 0 .. $columns - 1 ) {
shift @formatted_text;
}
}
return $output;
}
########################################################################
sub help {
########################################################################
my (%options) = @_;
my (@args) = @ARGV;
my $token;
return pod
if !@args && !$options{service};
my $service = $options{service};
my $module;
if ( @args == 2 ) {
( $service, $module ) = @args;
}
elsif ( @args == 1 && $service ) {
$module = $args[0];
}
else {
fetch_boto_services( $options{'botocore-path'} );
$options{service} //= 'all';
if ( $options{service} eq 'all' ) {
return print {*STDOUT} format_columns( text => [ sort keys %BOTO_SERVICES ] );
}
else {
my $description = get_service_descriptions( lc $options{service} );
my $operations
= $description->[0]->{ lc $options{service} }->{operations};
my $documentation
= $description->[0]->{ lc $options{service} }->{documentation};
$documentation = html2pod $documentation ;
my $available_commands
= format_columns( indent => 1, text => [ sort keys %{$operations} ] );
my %parameters = (
to_template_var('service') => $options{service},
to_template_var('documentation') => $documentation,
to_template_var('available_commands') => $available_commands,
);
my $help_text = <<'HELP';
=pod
=encoding utf8
=head1 NAME
@service@
=head1 DESCRIPTION
@documentation@
=head1 AVAILABLE COMMANDS
@available_commands@
=cut
HELP
my $pod = render_template( $help_text, \%parameters );
my $pod_parser = Pod::Text->new;
# yes, this outputs to STDOUT
return $pod_parser->parse_string_document($pod);
}
}
croak 'no service specified'
if !$service;
my $service_name = create_module_name($service);
my $file;
# try -s service or
for ( $service_name, $service ) {
my $class = sprintf 'Amazon::API::Botocore::Shape::%s::%s', $_, $module;
$file = require_class($class);
last if $file;
}
if ( !$file || !-e $file ) {
$file = require_class( sprintf 'Amazon::API::%s::%s', $service_name, $module );
}
$module = undef;
croak "no pod available\n"
if !$file || !-e $file;
if ( $options{pager} ) {
$token = eval {
require IO::Pager;
IO::Pager::open( *STDOUT, '|-:utf8', 'Unbuffered' );
};
}
my $pod = get_pod_section $file;
print {*STDOUT} "$pod\n";
return $EMPTY;
}
########################################################################
sub main {
########################################################################
my %options = (
'botocore-path' => $ENV{BOTOCORE_PATH} || getcwd,
tidy => $FALSE,
pod => $TRUE,
pager => $TRUE,
'output-path' => getcwd,
);
if ( $ENV{DEBUG} ) {
print {*STDERR} Dumper( \@ARGV );
}
my @option_defs = qw(
botocore-path|b=s
help|h
module-name|m=s
output-path|o=s
service|s=s
tidy|t!
pod|p!
pager|P!
);
GetOptions( \%options, @option_defs );
$options{command} = shift @ARGV;
$options{'extra-args'} = \@ARGV;
if ( $options{help} || !$options{command} ) {
$options{command} = 'help';
}
if ( !$options{'module-name'} && $options{service} ) {
$options{'module-name'} = create_module_name( $options{service} );
}
if ( $options{'output-path'} eq $DASH ) {
delete $options{'output-path'};
}
if ( $options{command} ne 'help' ) {
if ( $options{'output-path'} && $options{'output-path'} =~ /^[.]/xsm ) {
my $cwd = getcwd;
$options{'output-path'} =~ s/^[.]/$cwd/xsm;
}
elsif ( $options{'output-path'} && $options{'module-name'} ) {
my $module_path = sprintf '%s/Amazon/API/%s', @options{qw(output-path module-name)};
if ( !-d $module_path ) {
croak "could not create path: $module_path\n"
if !make_path $module_path;
}
}
}
my %handlers = (
'dump-service' => \&dump_service,
'dump' => \&dump_service,
'describe' => \&dump_service,
'create-stub' => \&create_stub,
'create-stubs' => \&create_stub,
'create-shapes' => \&create_service_shapes,
'create-shape' => \&create_service_shapes,
'help' => \&help,
);
croak sprintf 'not a valid command [%s]', $options{command}
if !$handlers{ $options{command} };
exit !$handlers{ $options{command} }->(%options);
}
1;
__DATA__
package @package_name@;
# Autogenerated by @program_name@ @program_version@ at @timestamp@
use strict;
use warnings;
use parent qw( Amazon::API );
our @API_METHODS = qw(
@actions@
);
our $VERSION = '2.1.4';
sub new {
my ( $class, @options ) = @_;
$class = ref($class) || $class;
my %options = ref $options[0] ? %{ $options[0] } : @options;
my $self = $class->SUPER::new(
{ service => '@service@',
endpoint_prefix => '@endpoint_prefix@',
version => '@version@',
target_prefix => '@target_prefix@',
api_methods => \@API_METHODS,
content_type => '@content_type@',
botocore_metadata => @metadata@,
botocore_operations => @operations@,
botocore_shapes => @shapes@,
debug => $ENV{DEBUG} // 0,
decode_always => 1,
paginators => @paginators@,
%options
}
);
# global services should be signed with us-east-1 region
if ( defined $self->get_botocore_metadata->{globalEndpoint} ) {
$self->set_region('us-east-1');
}
return $self;
}
1;
@end@
=pod
=encoding utf8
=head1 NAME
@package_name@
=head1 DESCRIPTION
@description@
=head1 VERSION
Version @program_version@
=head1 METHODS AND SUBROUTINES
@methods@
=head1 NOTES
Autogenerated by @program_name@ at @timestamp@
=head1 LICENSE AND COPYRIGHT
This module is free software it may be used, redistributed and/or
modified under the same terms as Perl itself.
=cut