@@ -, +, @@ SMS::Send drivers #.....your implementation..... # Throw an exception in case of a connection error # $connError can be for example: ($curl->{'retcode'} == 6) # cURL code 6: CURLE_COULDNT_RESOLVE_HOST if ($connError){ Koha::Exception::ConnectionFailed->throw(error => "Connection failed"); } #.....your implementation..... -2. Set system preference SMSSendDriver to Example::ExceptionExample -1. Enable system preference EnhancedMessagingPreferences --- C4/Letters.pm | 47 ++++++++++++++----- C4/SMS.pm | 23 ++++++---- SMS/Send/Example/ExceptionExample.pm | 87 ++++++++++++++++++++++++++++++++++++ t/db_dependent/Letters.t | 32 ++++++++++++- 4 files changed, 168 insertions(+), 21 deletions(-) create mode 100644 SMS/Send/Example/ExceptionExample.pm --- a/C4/Letters.pm +++ a/C4/Letters.pm @@ -35,6 +35,8 @@ use Date::Calc qw( Add_Delta_Days ); use Encode; use Carp; use Koha::Email; +use Scalar::Util qw( blessed ); +use Try::Tiny; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @@ -947,6 +949,7 @@ sub SendQueuedMessages { my $unsent_messages = _get_unsent_messages(); MESSAGE: foreach my $message ( @$unsent_messages ) { + # warn Data::Dumper->Dump( [ $message ], [ 'message' ] ); warn sprintf( 'sending %s message to patron: %s', $message->{'message_transport_type'}, @@ -954,11 +957,16 @@ sub SendQueuedMessages { if $params->{'verbose'} or $debug; # This is just begging for subclassing next MESSAGE if ( lc($message->{'message_transport_type'}) eq 'rss' ); - if ( lc( $message->{'message_transport_type'} ) eq 'email' ) { - _send_message_by_email( $message, $params->{'username'}, $params->{'password'}, $params->{'method'} ); - } - elsif ( lc( $message->{'message_transport_type'} ) eq 'sms' ) { - _send_message_by_sms( $message ); + eval { + if ( lc( $message->{'message_transport_type'} ) eq 'email' ) { + _send_message_by_email( $message, $params->{'username'}, $params->{'password'}, $params->{'method'} ); + } + elsif ( lc( $message->{'message_transport_type'} ) eq 'sms' ) { + _send_message_by_sms( $message ); + } + }; + if ($@) { + warn $@; } } return scalar( @$unsent_messages ); @@ -1262,12 +1270,29 @@ sub _send_message_by_sms { return; } - my $success = C4::SMS->send_sms( { destination => $member->{'smsalertnumber'}, - message => $message->{'content'}, - } ); - _set_message_status( { message_id => $message->{'message_id'}, - status => ($success ? 'sent' : 'failed') } ); - return $success; + try { + my $success = C4::SMS->send_sms( { destination => $member->{'smsalertnumber'}, + message => $message->{'content'}, + } ); + _set_message_status( { message_id => $message->{'message_id'}, + status => ($success ? 'sent' : 'failed') } ); + return $success; + } catch { + if (blessed($_)){ + if ($_->isa('Koha::Exception::ConnectionFailed')){ + # Don't do anything, the message will stay in + # 'pending' status and will be resent again later. + } + else { + $_->rethrow(); + } + } + else { + die $_; + } + }; + + return; } sub _update_message_to_address { --- a/C4/SMS.pm +++ a/C4/SMS.pm @@ -36,6 +36,8 @@ use strict; use warnings; use C4::Context; +use Try::Tiny; +use Scalar::Util qw ( blessed ); use vars qw( $VERSION ); @@ -77,7 +79,10 @@ sub send_sms { # warn "using driver: $driver to send message to $params->{'destination'}"; my ($sent, $sender); - eval { + + #We might die because SMS::Send $driver is not defined or the sms-number has a bad format + #Catch those errors and fail the sms-sending gracefully. + try { # Create a sender $sender = SMS::Send->new( $driver, _login => C4::Context->preference('SMSSendUsername'), @@ -88,14 +93,16 @@ sub send_sms { $sent = $sender->send_sms( to => $params->{'destination'}, text => $params->{'message'}, ); + return $sent; + } catch { + if (blessed($_) && $_->can('rethrow')) { + $_->rethrow(); + } + else { + die $_; + } }; - #We might die because SMS::Send $driver is not defined or the sms-number has a bad format - #Catch those errors and fail the sms-sending gracefully. - if ($@) { - warn $@; - return; - } - # warn 'failure' unless $sent; + return $sent; } --- a/SMS/Send/Example/ExceptionExample.pm +++ a/SMS/Send/Example/ExceptionExample.pm @@ -0,0 +1,87 @@ +package SMS::Send::Example::ExceptionExample; + +=pod + +=head1 NAME + +SMS::Send::Example::ExceptionExample + +=head1 SYNOPSIS + + use Try::Tiny; + use Scalar::Util qw( blessed ); + + # Create a testing sender + my $send = SMS::Send->new( 'Example::ExceptionExample' ); + + # Send a message + try { + $send->send_sms( + text => 'Hi there', + to => '+61 (4) 1234 5678', + ); + } catch { + if (blessed($_) && $_->can('rethrow')){ + # handle exception + } else { + die $_; + } + } + +=head1 DESCRIPTION + + This SMS::Send module provides an example for how + to throw Koha::Exceptions in case of an error. + + Exceptions will be caught outside this module by + try-catch block. + +=cut + +use strict; +use SMS::Send::Driver (); +use Koha::Exception::ConnectionFailed; + +use vars qw{$VERSION @ISA}; +BEGIN { + $VERSION = '0.06'; + @ISA = 'SMS::Send::Driver'; +} + + + + + +##################################################################### +# Constructor + +sub new { + my $class = shift; + + my $self = bless {}, $class; + + $self->{_login} = "ned"; + $self->{_password} = "flanders"; + + return $self; +} + +sub send_sms { + # ... + # ... our imaginary cURL implementation of sending sms messages to gateway + # $curl = sendMessageWithcURL("http://url.com/send", { + # destination => $params->{to}, + # text => $params->{text} + # }); + # my $errorCode = $curl->{'retcode'}; + # Using cURL, our request produced error code 6, CURLE_COULDNT_RESOLVE_HOST + my $errorCode = 6; + + if ($errorCode == 6) { + Koha::Exception::ConnectionFailed->throw(error => "Connection failed"); + } + + return 1; +} + +1; --- a/t/db_dependent/Letters.t +++ a/t/db_dependent/Letters.t @@ -18,7 +18,7 @@ # along with Koha; if not, see . use Modern::Perl; -use Test::More tests => 65; +use Test::More tests => 68; use Test::MockModule; use Test::Warn; @@ -33,7 +33,6 @@ $module->mock( %mail = @_; } ); - use_ok('C4::Context'); use_ok('C4::Members'); use_ok('C4::Branch'); @@ -46,6 +45,16 @@ use Koha::DateUtils qw( dt_from_string output_pref ); use Koha::Acquisition::Order; use Koha::Acquisition::Bookseller; use Koha::Database; +use Koha::Exception::ConnectionFailed; + +my $c4sms = new Test::MockModule('C4::SMS'); +$c4sms->mock( + 'driver' => + sub { + warn "Fake SMS driver"; + return "Example::ExceptionExample"; + } +); my $dbh = C4::Context->dbh; @@ -136,6 +145,25 @@ is( 'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)' ); +# Test connectivity Exception (Bug 14791) +$message_id = C4::Letters::EnqueueLetter($my_message); +$messages = C4::Letters::GetQueuedMessages(); +is( @$messages, 2, 'second message stored for the borrower' ); +ModMember(borrowernumber => $borrowernumber, smsalertnumber => "+1234567890"); + +{ +warning_is { + $messages_processed = C4::Letters::SendQueuedMessages(); } + "Fake SMS driver", + "SMS sent using the mocked SMS::Send driver subroutine send_sms"; + +is( + $messages->[1]->{status}, + 'pending', + 'Message is still pending after SendQueuedMessages() because of network failure (bug 14791)' +); +} + # GetLetters my $letters = C4::Letters::GetLetters(); is( @$letters, 0, 'GetLetters returns the correct number of letters' ); --