| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 2 |  |  | 2 |  | 22363 | use strict; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 55 |  | 
| 2 | 2 |  |  | 2 |  | 10 | use warnings; | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 94 |  | 
| 3 |  |  |  |  |  |  | package Mail::SendGrid; | 
| 4 |  |  |  |  |  |  | $Mail::SendGrid::VERSION = '0.08'; | 
| 5 |  |  |  |  |  |  | # ABSTRACT: interface to SendGrid.com mail gateway APIs | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 2 |  |  | 2 |  | 39 | use 5.008; | 
|  | 2 |  |  |  |  | 14 |  | 
| 8 | 2 |  |  | 2 |  | 1410 | use Mouse 0.94; | 
|  | 2 |  |  |  |  | 62537 |  | 
|  | 2 |  |  |  |  | 13 |  | 
| 9 | 2 |  |  | 2 |  | 2888 | use HTTP::Tiny 0.013; | 
|  | 2 |  |  |  |  | 106567 |  | 
|  | 2 |  |  |  |  | 73 |  | 
| 10 | 2 |  |  | 2 |  | 1032 | use JSON 2.53; | 
|  | 2 |  |  |  |  | 15159 |  | 
|  | 2 |  |  |  |  | 16 |  | 
| 11 | 2 |  |  | 2 |  | 1830 | use URI::Escape 3.30; | 
|  | 2 |  |  |  |  | 2720 |  | 
|  | 2 |  |  |  |  | 130 |  | 
| 12 | 2 |  |  | 2 |  | 11 | use Carp 1.20; | 
|  | 2 |  |  |  |  | 31 |  | 
|  | 2 |  |  |  |  | 177 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 2 |  |  | 2 |  | 1070 | use Mail::SendGrid::Bounce; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 1565 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | has 'api_user'  => (is => 'ro', isa => 'Str', required => 1); | 
| 17 |  |  |  |  |  |  | has 'api_key'   => (is => 'ro', isa => 'Str', required => 1); | 
| 18 |  |  |  |  |  |  | has 'ua'        => (is => 'ro', default => sub { HTTP::Tiny->new(); }); | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | my %valid_params = | 
| 21 |  |  |  |  |  |  | ( | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | 'bounces.get' => | 
| 24 |  |  |  |  |  |  | { | 
| 25 |  |  |  |  |  |  | days       => '\d+', | 
| 26 |  |  |  |  |  |  | start_date => '\d\d\d\d-\d\d-\d\d', | 
| 27 |  |  |  |  |  |  | end_date   => '\d\d\d\d-\d\d-\d\d', | 
| 28 |  |  |  |  |  |  | limit      => '\d+', | 
| 29 |  |  |  |  |  |  | offset     => '\d+', | 
| 30 |  |  |  |  |  |  | type       => 'hard|soft', | 
| 31 |  |  |  |  |  |  | email      => '\S+@\S+', | 
| 32 |  |  |  |  |  |  | }, | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | 'bounces.delete' => | 
| 35 |  |  |  |  |  |  | { | 
| 36 |  |  |  |  |  |  | start_date => '\d\d\d\d-\d\d-\d\d', | 
| 37 |  |  |  |  |  |  | end_date   => '\d\d\d\d-\d\d-\d\d', | 
| 38 |  |  |  |  |  |  | type       => 'hard|soft', | 
| 39 |  |  |  |  |  |  | email      => '\S+@\S+', | 
| 40 |  |  |  |  |  |  | }, | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | ); | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | sub bounces | 
| 45 |  |  |  |  |  |  | { | 
| 46 | 0 |  |  | 0 | 1 |  | my $self     = shift; | 
| 47 | 0 |  |  |  |  |  | my %opts     = @_; | 
| 48 | 0 |  |  |  |  |  | my $response; | 
| 49 |  |  |  |  |  |  | my $url; | 
| 50 | 0 |  |  |  |  |  | my $bounce_list; | 
| 51 | 0 |  |  |  |  |  | my (@bounces, $bounce); | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 0 |  |  |  |  |  | $response = $self->_make_request('bounces.get', \%opts, { date => 1 }); | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 0 | 0 |  |  |  |  | if ($response->{success}) { | 
| 56 | 0 |  |  |  |  |  | $bounce_list = decode_json($response->{content}); | 
| 57 | 0 |  |  |  |  |  | foreach my $bounce_details (@{ $bounce_list }) { | 
|  | 0 |  |  |  |  |  |  | 
| 58 | 0 |  |  |  |  |  | $bounce = Mail::SendGrid::Bounce->new($bounce_details); | 
| 59 | 0 | 0 |  |  |  |  | push(@bounces, $bounce) if defined($bounce); | 
| 60 |  |  |  |  |  |  | } | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 0 |  |  |  |  |  | return @bounces; | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | sub delete_bounces | 
| 67 |  |  |  |  |  |  | { | 
| 68 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 69 | 0 |  |  |  |  |  | my %opts     = @_; | 
| 70 | 0 |  |  |  |  |  | my $base_uri = 'https://sendgrid.com/api/bounces.delete.json'; | 
| 71 | 0 |  |  |  |  |  | my $response; | 
| 72 |  |  |  |  |  |  | my $json; | 
| 73 | 0 |  |  |  |  |  | my $url; | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 0 |  |  |  |  |  | $response = $self->_make_request('bounces.delete', \%opts, {}); | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 0 | 0 |  |  |  |  | if ($response->{success}) { | 
| 78 | 0 |  |  |  |  |  | $json = decode_json($response->{content}); | 
| 79 | 0 | 0 |  |  |  |  | if ($json->{message} eq 'success') { | 
|  |  | 0 |  |  |  |  |  | 
| 80 | 0 |  |  |  |  |  | return 1; | 
| 81 |  |  |  |  |  |  | } elsif (exists($json->{message})) { | 
| 82 | 0 |  |  |  |  |  | carp "bounces.delete failed - error message: $json->{message}\n"; | 
| 83 |  |  |  |  |  |  | } else { | 
| 84 | 0 |  |  |  |  |  | carp "unexpected response from SendGrid: $response->{content}\n"; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 |  |  |  |  |  | return 0; | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | # | 
| 92 |  |  |  |  |  |  | # _make_request | 
| 93 |  |  |  |  |  |  | # | 
| 94 |  |  |  |  |  |  | # Helper function to build the URL and then make the request to SendGrid | 
| 95 |  |  |  |  |  |  | # $action is the part of the endpoint that identifies the action | 
| 96 |  |  |  |  |  |  | #   eg for getting bounces, the base URL is https://sendgrid.com/api/bounces.get.json | 
| 97 |  |  |  |  |  |  | #   and $action will be 'bounces.get' | 
| 98 |  |  |  |  |  |  | # $optref is a hash reference that contains any options passed by the caller of the | 
| 99 |  |  |  |  |  |  | #   public function | 
| 100 |  |  |  |  |  |  | # $defaults is a hashref containing any defaults that we want to mix in, | 
| 101 |  |  |  |  |  |  | #   regardless of what the user passed to the public function | 
| 102 |  |  |  |  |  |  | # | 
| 103 |  |  |  |  |  |  | sub _make_request | 
| 104 |  |  |  |  |  |  | { | 
| 105 | 0 |  |  | 0 |  |  | my $self     = shift; | 
| 106 | 0 |  |  |  |  |  | my $action   = shift; | 
| 107 | 0 |  |  |  |  |  | my $optref   = shift; | 
| 108 | 0 |  |  |  |  |  | my $defaults = shift; | 
| 109 | 0 |  |  |  |  |  | my %params   = ( | 
| 110 |  |  |  |  |  |  | api_user => $self->api_user, | 
| 111 |  |  |  |  |  |  | api_key  => $self->api_key, | 
| 112 |  |  |  |  |  |  | %$defaults, | 
| 113 |  |  |  |  |  |  | ); | 
| 114 | 0 |  |  |  |  |  | my $url      = 'https://sendgrid.com/api/'.$action.'.json'; | 
| 115 | 0 |  |  |  |  |  | my $response; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 0 |  |  |  |  |  | foreach my $opt (keys %$optref) { | 
| 118 | 0 | 0 |  |  |  |  | if (not exists($valid_params{$action}->{$opt})) { | 
| 119 | 0 |  |  |  |  |  | carp "Mail::SendGrid unknown parameter '$opt' for $action"; | 
| 120 | 0 |  |  |  |  |  | return 0; | 
| 121 |  |  |  |  |  |  | } | 
| 122 | 0 | 0 | 0 |  |  |  | if ((not defined($optref->{$opt})) || ($optref->{$opt} !~ /^($valid_params{$action}->{$opt})$/)) { | 
| 123 | 0 |  |  |  |  |  | carp "Mail::SendGrid invalid value '$optref->{$opt}' for parameter '$opt' to $action"; | 
| 124 | 0 |  |  |  |  |  | return 0; | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 0 |  |  |  |  |  | $params{$opt} = $optref->{$opt}; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 0 |  |  |  |  |  | $url .= '?'.join('&', map { $_.'='.uri_escape($params{$_}) } keys %params); | 
|  | 0 |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  |  | 
| 131 | 0 |  |  |  |  |  | $response = $self->ua->get($url); | 
| 132 | 0 | 0 |  |  |  |  | if (not $response->{success}) { | 
| 133 | 0 |  |  |  |  |  | carp __PACKAGE__, " : $action HTTP request failed\n", | 
| 134 |  |  |  |  |  |  | " status code = $response->{status}\n", | 
| 135 |  |  |  |  |  |  | " reason      = $response->{reason}\n"; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 | 0 |  |  |  |  |  | return $response; | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | 1; | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | __END__ |