Amazon-DynamoDB
view release on metacpan or search on metacpan
lib/Amazon/DynamoDB/20120810.pm view on Meta::CPAN
package Amazon::DynamoDB::20120810;
$Amazon::DynamoDB::20120810::VERSION = '0.35';
use strict;
use warnings;
use Future;
use Future::Utils qw(repeat try_repeat);
use POSIX qw(strftime);
use JSON::MaybeXS qw(decode_json encode_json);
use MIME::Base64;
use List::Util;
use List::MoreUtils;
use B qw(svref_2object);
use HTTP::Request;
use Kavorka;
use Amazon::DynamoDB::Types;
use Type::Registry;
use VM::EC2::Security::CredentialCache;
use AWS::Signature4;
BEGIN {
my $reg = "Type::Registry"->for_me;
$reg->add_types(-Standard);
$reg->add_types("Amazon::DynamoDB::Types");
};
sub new {
my $class = shift;
bless { @_ }, $class
}
sub implementation { shift->{implementation} }
sub host { shift->{host} }
sub port { shift->{port} }
sub ssl { shift->{ssl} }
sub algorithm { 'AWS4-HMAC-SHA256' }
sub scope { shift->{scope} }
sub access_key { shift->{access_key} }
sub secret_key { shift->{secret_key} }
sub debug_failures { shift->{debug} }
sub max_retries { shift->{max_retries} }
method create_table(TableNameType :$TableName!,
Int :$ReadCapacityUnits = 2,
Int :$WriteCapacityUnits = 2,
AttributeDefinitionsType :$AttributeDefinitions,
KeySchemaType :$KeySchema!,
ArrayRef[GlobalSecondaryIndexType] :$GlobalSecondaryIndexes where { scalar(@$_) <= 5 },
ArrayRef[LocalSecondaryIndexType] :$LocalSecondaryIndexes
) {
my %payload = (
TableName => $TableName,
ProvisionedThroughput => {
ReadCapacityUnits => int($ReadCapacityUnits),
WriteCapacityUnits => int($WriteCapacityUnits),
}
);
if (defined($AttributeDefinitions)) {
foreach my $field_name (keys %$AttributeDefinitions) {
my $type = $AttributeDefinitions->{$field_name};
push @{$payload{AttributeDefinitions}}, {
AttributeName => $field_name,
AttributeType => $type // 'S',
}
}
}
$payload{KeySchema} = _create_key_schema($KeySchema, $AttributeDefinitions);
foreach my $index_record (['GlobalSecondaryIndexes', $GlobalSecondaryIndexes],
['LocalSecondaryIndexes', $LocalSecondaryIndexes]) {
my $index_type = $index_record->[0];
my $index = $index_record->[1];
if (defined($index)) {
foreach my $i (@$index) {
my $r = {
IndexName => $i->{IndexName},
(($index_type eq 'GlobalSecondaryIndexes') ?
(ProvisionedThroughput => {
ReadCapacityUnits => int($i->{ProvisionedThroughput}->{ReadCapacityUnits} // 1),
WriteCapacityUnits => int($i->{ProvisionedThroughput}->{WriteCapacityUnits} // 1),
}) : ()),
KeySchema => _create_key_schema($i->{KeySchema}, $AttributeDefinitions),
};
my $type = $i->{Projection}->{ProjectionType};
$r->{Projection}->{ProjectionType} = $type;
if (defined($i->{Projection}->{NonKeyAttributes})) {
my $attrs = $i->{Projection}->{NonKeyAttributes};
# Can't validate these attribute names since they aren't part of the key.
$r->{Projection}->{NonKeyAttributes} = $attrs;
lib/Amazon/DynamoDB/20120810.pm view on Meta::CPAN
die("Don't know how to decode type: $type");
}
}
fun _decode_item_attributes(Maybe[HashRef] $item) {
my $r;
foreach my $key (keys %$item) {
my $type = (keys %{$item->{$key}})[0];
my $value = $item->{$key}->{$type};
$r->{$key} = _decode_type_and_value($type, $item->{$key}->{$type});
}
return $r;
}
method _process_request(HTTP::Request $req, CodeRef $done?) {
my $current_retry = 0;
my $do_retry = 1;
try_repeat {
$do_retry = 0;
my $sleep_amount = 0;
if ($current_retry > 0) {
$sleep_amount = (2 ** $current_retry * 50)/1000;
}
my $complete = sub {
$self->_request($req)->transform(
fail => sub {
my ($status, $resp, $req)= @_;
my $r;
if (defined($resp) && defined($resp->code)) {
if ($resp->code == 500) {
$do_retry = 1;
$current_retry++;
} elsif ($resp->code == 400) {
my $json = $resp->can('decoded_content')
? $resp->decoded_content
: $resp->body; # Mojo
$r = decode_json($json);
if ($r->{__type} =~ /ProvisionedThroughputExceededException$/) {
# Need to sleep
$do_retry = 1;
$current_retry++;
} else {
# extract the type into a better prettyier name.
if ($r->{__type} =~ /^com\.amazonaws\.dynamodb\.v20120810#(.+)$/) {
$r->{type} = $1;
}
}
}
}
if (defined($self->max_retries()) && $current_retry > $self->max_retries()) {
$do_retry = 0;
}
if (!$do_retry) {
if ($self->debug_failures()) {
print "DynamoDB Failure: $status\n";
if (defined($resp)) {
print "response:\n";
print $resp->as_string() . "\n";
}
if (defined($req)) {
print "Request:\n";
print $req->as_string() . "\n";
}
}
return $r || $status;
}
},
done => $done);
};
if ($sleep_amount > 0) {
$self->{implementation}->delay($sleep_amount)->then($complete);
} else {
$complete->();
}
} until => sub { !$do_retry };
}
my $encode_key = sub {
my $source = shift;
my $r;
foreach my $k (keys %$source) {
my $v = $source->{$k};
# There is no sense in encoding undefined values or values that
# are the empty string.
if (defined($v) && $v ne '') {
# Reference $source->{$k} since the earlier test may cause
# the value to be stringified.
$r->{$k} = { _encode_type_and_value($source->{$k}) };
}
}
return $r;
};
fun _encode_attribute_value_list(Any $value_list, Str $compare_op) {
if ($compare_op =~ /^(EQ|NE|LE|LT|GE|GT|CONTAINS|NOT_CONTAINS|BEGINS_WITH)$/) {
defined($value_list) || Carp::confess("No defined value for comparison operator: $compare_op");
$value_list = [ { _encode_type_and_value($value_list) } ];
} elsif ($compare_op eq 'IN') {
if (!ref($value_list)) {
$value_list = [$value_list];
}
$value_list = [ map { { _encode_type_and_value($_) } } @$value_list];
} elsif ($compare_op eq 'BETWEEN') {
ref($value_list) eq 'ARRAY' || Carp::confess("Use of BETWEEN comparison operator requires an array");
scalar(@$value_list) == 2 || Carp::confess("BETWEEN comparison operator requires two values");
$value_list = [ map { { _encode_type_and_value($_) } } @$value_list];
}
return $value_list;
}
my $encode_filter = sub {
my $source = shift;
lib/Amazon/DynamoDB/20120810.pm view on Meta::CPAN
fun _create_key_schema(ArrayRef $source, HashRef $known_fields) {
defined($source) || die("No source passed to create_key_schema");
defined($known_fields) || die("No known fields passed to create_key_schmea");
my @r;
foreach my $field_name (@$source) {
defined($known_fields->{$field_name}) || Carp::confess("Unknown field specified '$field_name' in schema, must be defined in fields. schema:" . Data::Dumper->Dump([$source]));
push @r, {
AttributeName => $field_name,
KeyType => (scalar(@r) ? 'RANGE' : 'HASH')
};
}
return \@r;
};
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Amazon::DynamoDB::20120810
=head1 VERSION
version 0.35
=head1 DESCRIPTION
=head2 new
Instantiates the API object.
Expects the following named parameters:
=over 4
=item * implementation - the object which provides a Future-returning C<request> method,
see L<Amazon::DynamoDB::NaHTTP> for example.
=item * host - the host (IP or hostname) to communicate with
=item * port - the port to use for HTTP(S) requests
=item * ssl - true for HTTPS, false for HTTP
=item * algorithm - which signing algorithm to use, default AWS4-HMAC-SHA256
=item * scope - the scope for requests, typically C<region/host/aws4_request>
=item * access_key - the access key for signing requests
=item * secret_key - the secret key for signing requests
=item * debug_failures - print errors if they occur
=item * max_retries - maximum number of retries for a request
=back
=head2 create_table
Creates a new table. It may take some time before the table is marked
as active - use L</wait_for_table_status> to poll until the status changes.
Amazon Documentation:
L<http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html>
$ddb->create_table(
TableName => $table_name,
ReadCapacityUnits => 2,
WriteCapacityUnits => 2,
AttributeDefinitions => {
user_id => 'N',
date => 'N',
},
KeySchema => ['user_id', 'date'],
LocalSecondaryIndexes => [
{
IndexName => 'UserDateIndex',
KeySchema => ['user_id', 'date'],
Projection => {
ProjectionType => 'KEYS_ONLY',
},
ProvisionedThroughput => {
ReadCapacityUnits => 2,
WriteCapacityUnits => 2,
}
}
]
);
=back
=head2 describe_table
Describes the given table.
Amazon Documentation:
L<http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html>
$ddb->describe_table(TableName => $table_name);
=head2 delete_table
Delete a table.
Amazon Documentation:
L<http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteTable.html>
$ddb->delete_table(TableName => $table_name)
( run in 3.023 seconds using v1.01-cache-2.11-cpan-df04353d9ac )