Bugzilla – Attachment 42674 Details for
Bug 14767
Message delivery notes from SMS Gateway provider reports
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 14767: SMS delivery notes from SMS Gateway provider's report
Bug-14767-SMS-delivery-notes-from-SMS-Gateway-prov.patch (text/plain), 17.76 KB, created by
Lari Taskula
on 2015-09-17 14:08:17 UTC
(
hide
)
Description:
Bug 14767: SMS delivery notes from SMS Gateway provider's report
Filename:
MIME Type:
Creator:
Lari Taskula
Created:
2015-09-17 14:08:17 UTC
Size:
17.76 KB
patch
obsolete
>From 0efe86600160b4dd14fac2db40aa55a828fd2f4b Mon Sep 17 00:00:00 2001 >From: Lari Taskula <larit@student.uef.fi> >Date: Thu, 17 Sep 2015 13:57:11 +0300 >Subject: [PATCH] Bug 14767: SMS delivery notes from SMS Gateway provider's > report > >Two-phase method to receive delivery notes from SMS Gateway provider. This is an >example how to receive Gateway reports and most likely does not apply for your >local provider. However, it should give you a good starting point to start >receiving SMS delivery notes with REST API with your own provider. > >Example driver is SMS::Send::Labyrintti::Driver > >Phase ONE (POST to provider) >-------------------------------- >In phase ONE, as normally, we make a HTTP POST to our SMS Gateway provider and >give them the details of SMS. They will instantly respond if the message was >placed for delivery. If not, there must have been some error and we will throw a >Koha::Exception::SMSDeliveryFailure. This will be caught by C4::Letters via >C4::SMS. Message in the queue will be set as "failed" and the reason for failure >will be added to delivery_note accordingly. If the delivery was succesful, >we set the message as "sent" like before and continue to phase TWO. > >Phase TWO (provider to REST API) >-------------------------------- >In phase TWO, the message has been placed for delivery at our SMS Gateway >provider. When the message has been processed and sent at their end, they will >send us a POST request. We have added this into our Swagger definitions. If you >want to limit the REST endpoint only to your provider, please see koha-httpd.conf >where we will allow this endpoint to only certain IPs and block the rest. > >If the report says the message delivery was successful, we don't change anything. >However, if the report says that the message delivery was failed, we will change >the message status from "sent" to "failed" and add the reason of failure into >delivery_note. > >Includes REST tests. > >Endpoints introduced in this patch: >POST /api/v1/messages/{messagenumber}/report/labyrintti (for receiving the report) >--- > C4/Installer/PerlDependencies.pm | 5 ++ > C4/Letters.pm | 6 ++ > C4/SMS.pm | 1 + > Koha/Exception/SMSDeliveryFailure.pm | 28 ++++++++ > Koha/REST/V1/Messages/Reports.pm | 37 ++++++++++ > SMS/Send/Labyrintti/Driver.pm | 129 +++++++++++++++++++++++++++++++++++ > api/v1/swagger.json | 50 ++++++++++++++ > etc/koha-conf.xml | 14 ++++ > etc/koha-httpd.conf | 18 +++++ > t/db_dependent/Api/V1/Messages.pm | 82 ++++++++++++++++++++++ > 10 files changed, 370 insertions(+) > create mode 100644 Koha/Exception/SMSDeliveryFailure.pm > create mode 100644 Koha/REST/V1/Messages/Reports.pm > create mode 100644 SMS/Send/Labyrintti/Driver.pm > >diff --git a/C4/Installer/PerlDependencies.pm b/C4/Installer/PerlDependencies.pm >index e00110a..f5c7389 100644 >--- a/C4/Installer/PerlDependencies.pm >+++ b/C4/Installer/PerlDependencies.pm >@@ -204,6 +204,11 @@ our $PERL_DEPS = { > 'required' => '1', > 'min_ver' => '2.033' > }, >+ 'LWP::Curl' => { >+ 'usage' => 'Curl', >+ 'required' => '0', >+ 'min_ver' => '0.12', >+ }, > 'MIME::Base64' => { > 'usage' => 'Core', > 'required' => '1', >diff --git a/C4/Letters.pm b/C4/Letters.pm >index 0a5ec5a..c9d8494 100644 >--- a/C4/Letters.pm >+++ b/C4/Letters.pm >@@ -1456,6 +1456,12 @@ sub _send_message_by_sms { > status => 'pending', > delivery_note => 'Connection failed. Attempting to resend.' } ); > } >+ elsif ($_->isa('Koha::Exception::SMSDeliveryFailure')){ >+ # SMS delivery was unsuccessful. Set message to failed and give a delivery note >+ _set_message_status ( { message_id => $message->{'message_id'}, >+ status => 'failed', >+ delivery_note => $_->error } ); >+ } > else { > $_->rethrow(); > } >diff --git a/C4/SMS.pm b/C4/SMS.pm >index a4ee9c7..c5573ea 100644 >--- a/C4/SMS.pm >+++ b/C4/SMS.pm >@@ -92,6 +92,7 @@ sub send_sms { > # Send a message > $sent = $sender->send_sms( to => $params->{'destination'}, > text => $params->{'message'}, >+ _message_id => $params->{'message_id'}, > ); > return $sent; > } catch { >diff --git a/Koha/Exception/SMSDeliveryFailure.pm b/Koha/Exception/SMSDeliveryFailure.pm >new file mode 100644 >index 0000000..0c623a9 >--- /dev/null >+++ b/Koha/Exception/SMSDeliveryFailure.pm >@@ -0,0 +1,28 @@ >+package Koha::Exception::SMSDeliveryFailure; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Exception::Class ( >+ 'Koha::Exception::SMSDeliveryFailure' => { >+ description => 'SMS delivery was failed.', >+ }, >+); >+ >+return 1; >\ No newline at end of file >diff --git a/Koha/REST/V1/Messages/Reports.pm b/Koha/REST/V1/Messages/Reports.pm >new file mode 100644 >index 0000000..5927161 >--- /dev/null >+++ b/Koha/REST/V1/Messages/Reports.pm >@@ -0,0 +1,37 @@ >+package Koha::REST::V1::Messages::Reports; >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use C4::Letters; >+use Data::Dumper; >+ >+ >+ >+=head2 list_messages($c, $args, $cb) >+ >+Lists all messages from message_queue. >+ >+=cut >+sub create_labyrintti_report { >+ my ($c, $args, $cb) = @_; >+ >+ my $message = C4::Letters::GetMessage($args->{'messagenumber'}); >+ >+ return $c->$cb({ error => "No messages found." }, 404) if not $message; >+ >+ # delivery was failed. edit status to failed and add delivery note >+ if ($args->{'status'} eq "ERROR") { >+ C4::Letters::UpdateQueuedMessage({ >+ message_id => $message->{message_id}, >+ status => 'failed', >+ delivery_note => $args->{message}, >+ }); >+ return $c->$cb('', 201); >+ } >+ >+ return $c->$cb('', 204); >+} >+ >+1; >diff --git a/SMS/Send/Labyrintti/Driver.pm b/SMS/Send/Labyrintti/Driver.pm >new file mode 100644 >index 0000000..45256b5 >--- /dev/null >+++ b/SMS/Send/Labyrintti/Driver.pm >@@ -0,0 +1,129 @@ >+=head IN THIS FILE >+ >+This module extends the SMS::Send::Driver interface >+to implement a driver compatible with the Labyrintti SMS Gateway HTTP interface. >+ >+Module parameters are sanitated against injection attacks. >+ >+Labyrintti responds: >+ >+ success >+ >+phone-number OK message-count description >++358401234567 OK 1 message accepted for sending >+ >+ failure >+ >+phone-number ERROR error-code message-count description >+e.g: 12345 ERROR 2 1 message failed: Too short phone number >+ >+=cut >+ >+package SMS::Send::Labyrintti::Driver; >+#use Modern::Perl; #Can't use this since SMS::Send uses hash keys starting with _ >+use utf8; >+use SMS::Send::Driver (); >+use LWP::Curl; >+use LWP::UserAgent; >+use URI::Escape; >+use C4::Context; >+use Encode; >+use Koha::Exception::ConnectionFailed; >+use Koha::Exception::SMSDeliveryFailure; >+ >+use vars qw{$VERSION @ISA}; >+BEGIN { >+ $VERSION = '0.06'; >+ @ISA = 'SMS::Send::Driver'; >+} >+ >+ >+##################################################################### >+# Constructor >+ >+sub new { >+ my $class = shift; >+ my $params = {@_}; >+ my $username = $params->{_login} ? $params->{_login} : C4::Context->config('smsProviders')->{'labyrintti'}->{'user'}; >+ my $password = $params->{_password} ? $params->{_password} : C4::Context->config('smsProviders')->{'labyrintti'}->{'passwd'}; >+ >+ if (! defined $username ) { >+ warn "->send_sms(_login) must be defined!"; >+ return; >+ } >+ if (! defined $password ) { >+ warn "->send_sms(_password) must be defined!"; >+ return; >+ } >+ >+ #Prevent injection attack >+ $self->{_login} =~ s/'//g; >+ $self->{_password} =~ s/'//g; >+ >+ # Create the object >+ my $self = bless {}, $class; >+ >+ $self->{UserAgent} = LWP::UserAgent->new(timeout => 5); >+ $self->{_login} = $username; >+ $self->{_password} = $password; >+ >+ return $self; >+} >+ >+sub send_sms { >+ my $self = shift; >+ my $params = {@_}; >+ my $message = $params->{text}; >+ my $recipientNumber = $params->{to}; >+ >+ if (! defined $message ) { >+ warn "->send_sms(text) must be defined!"; >+ return; >+ } >+ if (! defined $recipientNumber ) { >+ warn "->send_sms(to) must be defined!"; >+ return; >+ } >+ >+ #Prevent injection attack! >+ $recipientNumber =~ s/'//g; >+ $message =~ s/(")|(\$\()|(`)/\\"/g; #Sanitate " so it won't break the system( iconv'ed curl command ) >+ >+ >+ >+ my $base_url = "https://gw.labyrintti.com:28443/sendsms"; >+ my $parameters = { >+ 'user' => $self->{_login}, >+ 'password' => $self->{_password}, >+ 'dests' => $recipientNumber, >+ 'text' => $message, >+ 'unicode' => 'yes', >+ }; >+ >+ my $report_url = C4::Context->config('smsProviders')->{'labyrintti'}->{'reportUrl'}; >+ if ($report_url) { >+ my $msg_id = $params->{_message_id}; >+ $report_url =~ s/\{messagenumber\}/$msg_id/g; >+ $parameters->{'report'} = $report_url; >+ } >+ >+ my $lwpcurl = LWP::Curl->new(auto_encode => 0); >+ my $return = $lwpcurl->post($base_url, $parameters); >+ >+ if ($lwpcurl->{retcode} == 6) { >+ Koha::Exception::ConnectionFailed->throw(error => "Connection failed"); >+ } >+ >+ my $delivery_note = $return; >+ >+ return 1 if ($return =~ m/OK [1-9](\d*)?/); >+ >+ # remove everything except the delivery note >+ $delivery_note =~ s/^(.*)message\sfailed:\s*//g; >+ >+ # pass on the error by throwing an exception - it will be eventually caught >+ # in C4::Letters::_send_message_by_sms() >+ Koha::Exception::SMSDeliveryFailure->throw(error => $delivery_note); >+} >+ >+1; >diff --git a/api/v1/swagger.json b/api/v1/swagger.json >index 5d37123..2e78bf3 100644 >--- a/api/v1/swagger.json >+++ b/api/v1/swagger.json >@@ -507,6 +507,56 @@ > } > } > } >+ }, >+ "/messages/{messagenumber}/report/labyrintti": { >+ "post": { >+ "x-mojo-controller": "Koha::REST::V1::Messages::Reports", >+ "operationId": "createLabyrinttiReport", >+ "tags": ["messages"], >+ "parameters": [ >+ { >+ "$ref": "#/parameters/messagenumberPathParam" >+ }, >+ { >+ "name": "status", >+ "in": "formData", >+ "type": "string", >+ "description": "Status of the delivery", >+ "required": true >+ }, >+ { >+ "name": "message", >+ "in": "formData", >+ "type": "string", >+ "description": "Delivery notes", >+ "required": true >+ } >+ ], >+ "consumes": ["application/x-www-form-urlencoded"], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "201": { >+ "description": "Response for creating a new report. Required status ERROR", >+ "schema": { >+ "type": "string" >+ } >+ }, >+ "204": { >+ "description": "Response for receiving delivery report with status OK", >+ "schema": { >+ "type": "string" >+ } >+ }, >+ "404": { >+ "description": "Message not found", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } >+ } > } > }, > "definitions": { >diff --git a/etc/koha-conf.xml b/etc/koha-conf.xml >index 962e031..e56c6f0 100644 >--- a/etc/koha-conf.xml >+++ b/etc/koha-conf.xml >@@ -145,5 +145,19 @@ __PAZPAR2_TOGGLE_XML_POST__ > </rest> > </testservers> > >+ <smsProviders> >+ <labyrintti> >+ <!-- Login details for SMS Gateway service --> >+ <user></user> >+ <passwd></passwd> >+ <!-- Report URL is the page where your SMS Gateway provider will send a report >+ about the delivery of the message. >+ >+ With REST API, it should be >+ http://mydomain.com/api/v1/messages/{messagenumber}/report/_your_gateway_ >+ --> >+ <reportUrl></reportUrl> >+ </labyrintti> >+ </smsProviders> > </config> > </yazgfs> >diff --git a/etc/koha-httpd.conf b/etc/koha-httpd.conf >index 0b8078f..6c74c56 100644 >--- a/etc/koha-httpd.conf >+++ b/etc/koha-httpd.conf >@@ -39,6 +39,15 @@ > ProxyPassReverse /api/v1/ http://localhost:8081/api/v1/ > ##REVERSE PROXYING OK > >+# Uncomment the following block to restrict access for changing >+# message status via reports to only from certain IPs. >+# <LocationMatch "/v1/messages/\d+/report"> >+# Order Deny,Allow >+# Deny from all >+# Allow from 101.123.213.10 >+# Allow from 101.123.213.11 >+# </LocationMatch> >+ > <Directory "__OPAC_WWW_DIR__"> > Options -Indexes > </Directory> >@@ -168,6 +177,15 @@ > ProxyPassReverse /api/v1/ http://localhost:8081/api/v1/ > ##REVERSE PROXYING OK > >+# Uncomment the following block to restrict access for changing >+# message status via reports to only from certain IPs. >+# <LocationMatch "/v1/messages/\d+/report"> >+# Order Deny,Allow >+# Deny from all >+# Allow from 101.123.213.10 >+# Allow from 101.123.213.11 >+# </LocationMatch> >+ > <Directory "__INTRANET_WWW_DIR__"> > Options -Indexes > </Directory> >diff --git a/t/db_dependent/Api/V1/Messages.pm b/t/db_dependent/Api/V1/Messages.pm >index 359335c..5d262df 100644 >--- a/t/db_dependent/Api/V1/Messages.pm >+++ b/t/db_dependent/Api/V1/Messages.pm >@@ -269,4 +269,86 @@ sub delete_n_404 { > return 1; > } > >+sub post_n_reportlabyrintti404 { >+ my ($class, $restTest, $driver) = @_; >+ my $testContext = $restTest->get_testContext(); >+ my $activeUser = $restTest->get_activeBorrower(); >+ my $path = $restTest->get_routePath(); >+ >+ $path =~ s/\{messagenumber\}/12334893298007/; >+ $driver->post_ok( >+ $path => { Accept => 'application/x-www-form-urlencoded'} => >+ form => { >+ status => "OK", >+ message => "Message delivery succesful", >+ }); >+ $driver->status_is(404); >+ >+ return 1; >+} >+ >+sub post_n_reportlabyrintti204 { >+ my ($class, $restTest, $driver) = @_; >+ my $testContext = $restTest->get_testContext(); >+ my $activeUser = $restTest->get_activeBorrower(); >+ my $path = $restTest->get_routePath(); >+ >+ #Create the test context. >+ my $messages = t::lib::TestObjects::MessageQueueFactory->createTestGroup( >+ { >+ subject => "The quick brown fox", >+ content => "Jumps over the lazy dog.", >+ cardnumber => $activeUser->{'_result'}->{'_column_data'}->{cardnumber}, >+ message_transport_type => 'sms', >+ from_address => '11A001@example.com', >+ }, undef, $testContext, undef, undef); >+ >+ my $messagenumber = $messages->{'11A001@example.com'}->{message_id}; >+ $path =~ s/\{messagenumber\}/$messagenumber/; >+ $driver->post_ok( >+ $path => { Accept => 'application/x-www-form-urlencoded'} => >+ form => { >+ status => "OK", >+ message => "Message delivery succesful", >+ } >+ ); >+ $driver->status_is(204); >+ >+ t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); >+ return 1; >+} >+ >+sub post_n_reportlabyrintti201 { >+ my ($class, $restTest, $driver) = @_; >+ my $testContext = $restTest->get_testContext(); >+ my $activeUser = $restTest->get_activeBorrower(); >+ my $path = $restTest->get_routePath(); >+ >+ #Create the test context. >+ my $messages = t::lib::TestObjects::MessageQueueFactory->createTestGroup( >+ { >+ subject => "The quick brown fox", >+ content => "Jumps over the lazy dog.", >+ cardnumber => $activeUser->{'_result'}->{'_column_data'}->{cardnumber}, >+ message_transport_type => 'sms', >+ from_address => '11A001@example.com', >+ }, undef, $testContext, undef, undef); >+ >+ my $messagenumber = $messages->{'11A001@example.com'}->{message_id}; >+ $path =~ s/\{messagenumber\}/$messagenumber/; >+ $driver->post_ok( >+ $path => { Accept => 'application/x-www-form-urlencoded'} => >+ form => { >+ status => "ERROR", >+ message => "Test error delivery note", >+ } >+ ); >+ $driver->status_is(201); >+ my $message = C4::Letters::GetMessage($messagenumber); >+ is($message->{delivery_note}, "Test error delivery note", "Delivery note for failed SMS received successfully."); >+ >+ t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); >+ return 1; >+} >+ > 1; >\ No newline at end of file >-- >1.9.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 14767
:
42674
|
42695
|
42805