New implementation of bundle

This commit is contained in:
Javier Hernández Gil 2014-03-31 00:20:02 +02:00
parent f01d7194fe
commit cae1a2f071
17 changed files with 619 additions and 199 deletions

View File

@ -0,0 +1,34 @@
<?php
namespace Jhg\NexmoBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Javi Hernández
*/
class AccountBalanceCommand extends ContainerAwareCommand
{
/**
* @see Command
*/
protected function configure() {
$this
->setName('nexmo:account:balance')
->setDescription('Gets account balance')
->setHelp("The <info>nexmo:account:balance</info> command gets Nexmo API account balance");
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$account = $this->getContainer()->get('jhg_nexmo.account');
$balance = $account->balance();
$output->writeln(sprintf('Account balance: %f',$balance));
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Jhg\NexmoBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Javi Hernández
*/
class SmsPricingCommand extends ContainerAwareCommand
{
/**
* @see Command
*/
protected function configure() {
$this
->setName('nexmo:sms:pricing')
->setDescription('Gets sms price for given country')
->setDefinition(array(
new InputArgument('country', InputArgument::REQUIRED, 'The country code'),
))
->setHelp("The <info>nexmo:sms:pricing</info> command gets Nexmo API SMS pricing for a given country");
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$country = $input->getArgument('country');
$account = $this->getContainer()->get('jhg_nexmo.account');
$price = $account->smsPricing($country);
if($price===false) {
throw new \Exception("Country not valid");
} else {
$output->writeln(sprintf('SMS sending price for "%s": %f',$country,$price));
}
}
}

View File

@ -8,15 +8,14 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Javi Hernández Gil <bilbo@deverbena.com>
* @author Javi Hernández
*/
class SmsSendCommand extends ContainerAwareCommand
{
/**
* @see Command
*/
protected function configure()
{
protected function configure() {
$this
->setName('nexmo:sms:send')
->setDescription('Send a SMS message')
@ -24,6 +23,7 @@ class SmsSendCommand extends ContainerAwareCommand
new InputArgument('number', InputArgument::REQUIRED, 'The number'),
new InputArgument('fromName', InputArgument::REQUIRED, 'The name shown as origin'),
new InputArgument('message', InputArgument::REQUIRED, 'The message'),
new InputOption('report','r',InputOption::VALUE_OPTIONAL,'Ask for status report'),
))
->setHelp(<<<EOT
The <info>nexmo:sms:send</info> command sends a SMS message through Nexmo API
@ -35,18 +35,23 @@ EOT
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
protected function execute(InputInterface $input, OutputInterface $output) {
$number = $input->getArgument('number');
$fromName = $input->getArgument('fromName');
$message = $input->getArgument('message');
$report = (int)$input->getOption('report');
$sender = $this->getContainer()->get('jhg_nexmo.sms.sender');
$smsManager = $this->getContainer()->get('jhg_nexmo_sms');
if($sender->send($number,$fromName,$message,null,0)) {
$output->writeln(sprintf('SMS send to %u from %s: "%s"',$number,$fromName,$message));
if($response = $smsManager->sendText($number,$message,$fromName,$report)) {
$output->writeln(sprintf('SMS send to %s from %s: "%s"',$number,$fromName,$message));
$output->writeln(sprintf(' message id: %s',$response->getMessageId()));
$output->writeln(sprintf(' status: %s',$response->getStatus()));
$output->writeln(sprintf(' message price: %f',$response->getMessagePrice()));
$output->writeln(sprintf(' remaining balance: %f',$response->getRemainingBalance()));
$output->writeln(sprintf(' network: %u',$response->getNetwork()));
} else {
$output->writeln(sprintf('There was an error sending SMS to %u from %s: "%s"',$number,$fromName,$message));
$output->writeln(sprintf('There was an error sending SMS to %s from %s: "%s"',$number,$fromName,$message));
}
}

View File

@ -20,16 +20,28 @@ class Configuration implements ConfigurationInterface
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('jhg_nexmo');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
$rootNode
->children()
->scalarNode('api_key')->end()
->scalarNode('api_secret')->end()
->scalarNode('from_name')->end()
->booleanNode('disable_delivery')->end()
->scalarNode('api_key')
->isRequired()
->end()
->scalarNode('api_secret')
->isRequired()
->end()
->scalarNode('from_name')
->validate()
->ifTrue(function ($s) {
return (strlen($s)>11 || strlen($s)<2) && preg_match('/^[0-9a-z]{11}$/i', $s) !== 1;
})
->thenInvalid('Invalid from_name, only alphanumeric characters are allowed')
->end()
->end()
->booleanNode('disable_delivery')
->defaultFalse()
->end()
->end()
;

View File

@ -21,34 +21,10 @@ class JhgNexmoExtension extends Extension
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
if (!isset($config['api_key'])) {
throw new \InvalidArgumentException('The "api_key" option must be set for JhgNexmoBundle');
}
if (!isset($config['api_secret'])) {
throw new \InvalidArgumentException('The "api_secret" option must be set for JhgNexmoBundle');
}
$container->setParameter('jhg_nexmo.api_key', $config['api_key']);
$container->setParameter('jhg_nexmo.api_secret', $config['api_secret']);
if(isset($config['disable_delivery'])) {
$container->setParameter('jhg_nexmo.disable_delivery', $config['disable_delivery']);
}
if(isset($config['from_name'])) {
if (strlen($config['from_name'])>11) {
throw new \InvalidArgumentException('The "jhg_nexmo.from_name" option can not be larger than 11 characters');
}
if (!preg_match('/^[0-9a-z]{11}$/i', $config['from_name'])) {
throw new \InvalidArgumentException('The "jhg_nexmo.from_name" option only have alphanumeric characters');
}
$container->setParameter('jhg_nexmo.from_name', $config['from_name']);
} else {
$container->setParameter('jhg_nexmo.from_name', 'MyAppName');
}
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');

View File

@ -5,7 +5,7 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Bundle.
* @author Javi Hernández Gil <bilbo@deverbena.com>
* @author Javi Hernández
*/
class JhgNexmoBundle extends Bundle
{

View File

@ -0,0 +1,87 @@
<?php
namespace Jhg\NexmoBundle\Managers;
use Jhg\NexmoBundle\NexmoClient\NexmoClient;
/**
* Class AccountManager
* @package Jhg\NexmoBundle\Managers
* @Author Javi Hernández
*/
class AccountManager
{
/**
* @var \Jhg\NexmoBundle\NexmoClient\NexmoClient
*/
protected $nexmoClient;
/**
* @param NexmoClient $nexmoClient
*/
public function __construct(NexmoClient $nexmoClient) {
$this->nexmoClient = $nexmoClient;
}
/**
* @return bool|float - account balance | false on fail
*/
public function balance() {
$response = $this->nexmoClient->accountBalance();
return floatval($response['value']);
}
/**
* @param $country
* @return bool|float - sms pricing | false on fail
*/
public function smsPricing($country) {
$response = $this->nexmoClient->accountSmsPrice($country);
return floatval($response['mt']);
}
/**
* @todo Implement getCountryDialingCode method
* @param $country_code
* @throws \Exception
*/
public function getCountryDialingCode ($country_code) {
throw new \Exception(__METHOD__.' not yet implemented');
}
/**
* @todo Implement numbersList method
* @throws \Exception
*/
public function numbersList () {
throw new \Exception(__METHOD__.' not yet implemented');
}
/**
* @todo Implement numbersSearch method
* @param $country_code
* @param $pattern
* @throws \Exception
*/
public function numbersSearch ($country_code, $pattern) {
throw new \Exception(__METHOD__.' not yet implemented');
}
/**
* @todo Implement numbersBuy method
* @param $country_code
* @param $msisdn
* @throws \Exception
*/
public function numbersBuy ($country_code, $msisdn) {
throw new \Exception(__METHOD__.' not yet implemented');
}
/**
* @todo Implement numbersCancel method
* @throws \Exception
*/
public function numbersCancel() {
throw new \Exception(__METHOD__.' not yet implemented');
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Jhg\NexmoBundle\Managers;
use Jhg\NexmoBundle\NexmoClient\NexmoClient;
/**
* Class NumberManager
* @package Jhg\NexmoBundle\Managers
* @Author Javi Hernández
*/
class NumberManager
{
/**
* @var \Jhg\NexmoBundle\NexmoClient\NexmoClient
*/
protected $nexmoClient;
/**
* @param NexmoClient $nexmoClient
*/
public function __construct(NexmoClient $nexmoClient) {
$this->nexmoClient = $nexmoClient;
}
public function search() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function buy() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function cancel() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function update() {
throw new \Exception(__METHOD__.' not yet implemented');
}
}

67
Managers/SmsManager.php Normal file
View File

@ -0,0 +1,67 @@
<?php
namespace Jhg\NexmoBundle\Managers;
use Jhg\NexmoBundle\Model\SmsSendResponse;
use Jhg\NexmoBundle\NexmoClient\NexmoClient;
use Jhg\NexmoBundle\Utils\PhoneNumber;
/**
* Class SmsManager
* @package Jhg\NexmoBundle\Managers
* @Author Javi Hernández
*/
class SmsManager
{
/**
* @var \Jhg\NexmoBundle\NexmoClient\NexmoClient
*/
protected $nexmoClient;
/**
* @var string
*/
protected $defaultFromName;
/**
* @param NexmoClient $nexmoClient
* @param $defaultFromName
*/
public function __construct(NexmoClient $nexmoClient,$defaultFromName) {
$this->nexmoClient = $nexmoClient;
$this->defaultFromName = $defaultFromName;
}
/**
* @param string $number
* @param string $message
* @param null|string $fromName
* @param int $status_report_req
* @return mixed
*/
public function sendText($number,$message,$fromName=null,$status_report_req=0) {
$fromName = $fromName!==null ? $fromName : $this->defaultFromName;
$number = PhoneNumber::prefixFilter($number);
$response = $this->nexmoClient->sendTextMessage($fromName,$number,$message,$status_report_req);
return SmsSendResponse::createFromResponse($response);
}
public function sendBinary() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function sendWapPush() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function searchMessage() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function searchMessages() {
throw new \Exception(__METHOD__.' not yet implemented');
}
public function searchRejections() {
throw new \Exception(__METHOD__.' not yet implemented');
}
}

11
Model/Sms.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace Jhg\NexmoBundle\Model;
/**
* Class Sms
* @package Jhg\NexmoBundle\Model
* @Author Javi Hernández
*/
class Sms {
}

158
Model/SmsSendResponse.php Normal file
View File

@ -0,0 +1,158 @@
<?php
namespace Jhg\NexmoBundle\Model;
use MyProject\Proxies\__CG__\stdClass;
/**
* Class SmsSendResponse
* @package Jhg\NexmoBundle\Model
* @Author Javi Hernández
*/
class SmsSendResponse {
/**
* @var string
*/
protected $to;
/**
* @var string
*/
protected $messageId;
/**
* @var int
*/
protected $status;
/**
* @var float
*/
protected $remainingBalance;
/**
* @var float
*/
protected $messagePrice;
/**
* @var int
*/
protected $network;
/**
* @param stdClass $response
* @return SmsSendResponse
*/
public static function createFromResponse($response) {
$smsSendResponse = new SmsSendResponse();
$smsSendResponse->setTo($response['to']);
$smsSendResponse->setMessageId($response['message-id']);
$smsSendResponse->setStatus((int)$response['status']);
$smsSendResponse->setRemainingBalance(floatval($response['remaining-balance']));
$smsSendResponse->setMessagePrice(floatval($response['message-price']));
$smsSendResponse->setNetwork((int)$response['network']);
return $smsSendResponse;
}
/**
* @param string $messageId
*/
public function setMessageId($messageId)
{
$this->messageId = $messageId;
}
/**
* @return string
*/
public function getMessageId()
{
return $this->messageId;
}
/**
* @param float $messagePrice
*/
public function setMessagePrice($messagePrice)
{
$this->messagePrice = $messagePrice;
}
/**
* @return float
*/
public function getMessagePrice()
{
return $this->messagePrice;
}
/**
* @param int $network
*/
public function setNetwork($network)
{
$this->network = $network;
}
/**
* @return int
*/
public function getNetwork()
{
return $this->network;
}
/**
* @param float $remainingBalance
*/
public function setRemainingBalance($remainingBalance)
{
$this->remainingBalance = $remainingBalance;
}
/**
* @return float
*/
public function getRemainingBalance()
{
return $this->remainingBalance;
}
/**
* @param int $status
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* @return int
*/
public function getStatus()
{
return $this->status;
}
/**
* @param string $to
*/
public function setTo($to)
{
$this->to = $to;
}
/**
* @return string
*/
public function getTo()
{
return $this->to;
}
}

107
NexmoClient/NexmoClient.php Normal file
View File

@ -0,0 +1,107 @@
<?php
namespace Jhg\NexmoBundle\NexmoClient;
class NexmoClient {
/**
* @var string
*/
protected $rest_url;
/**
* @var string
*/
protected $api_key;
/**
* @var string
*/
protected $api_secret;
/**
* @var string
*/
protected $api_method;
/**
* @param $api_key
* @param $api_secret
* @param string $api_method GET|POST configured in Nexmo API preferences
*/
public function __construct($api_key,$api_secret,$api_method='GET') {
$this->rest_url = 'https://rest.nexmo.com';
$this->api_key = $api_key;
$this->api_secret = $api_secret;
$this->api_method = $api_method;
}
/**
* @param $url
* @param array $params
* @return array
*/
protected function jsonRequest($url,$params=array()) {
$params['api_key'] = $this->api_key;
$params['api_secret'] = $this->api_secret;
$request_url = $this->rest_url.'/'.trim($url,'/').'?'.http_build_query($params);
$request = curl_init($request_url);
curl_setopt($request,CURLOPT_RETURNTRANSFER,true );
curl_setopt($request,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($request, CURLOPT_HTTPHEADER,array('Accept: application/json'));
$response = curl_exec($request);
$curl_info = curl_getinfo($request);
$http_response_code = (int)$curl_info['http_code'];
curl_close($request);
switch($http_response_code) {
case 200:
return json_decode($response,true);
}
}
/**
* @example {"autoReload":false,"value":0.2}
* @return array
*/
public function accountBalance() {
return $this->jsonRequest('/account/get-balance');
}
/**
* @param $country
* @return array[country=ES,mt=0.060000,name=Spain,prefix=34]
*/
public function accountSmsPrice($country) {
return $this->jsonRequest('/account/get-pricing/outbound',array('country'=>$country));
}
/**
* @param string $fromName
* @param string $toNumber
* @param string $text
* @param int $status_report_req
* @return array
* @throws \Exception
*/
public function sendTextMessage($fromName,$toNumber,$text,$status_report_req=0) {
$params = array(
'from'=>$fromName,
'to'=>$toNumber,
'text'=>$text,
'status-report-req'=>$status_report_req,
);
$response = $this->jsonRequest('/sms/json',$params);
if((int)$response['messages'][0]['status']!=0) {
throw new \Exception($response['messages'][0]['error-text']);
}
return $response['messages'][0];
}
}

View File

@ -1,10 +1,16 @@
parameters:
services:
jhg_nexmo.sms_message:
class: Nexmo\NexmoMessage
jhg_nexmo_client:
class: Jhg\NexmoBundle\NexmoClient\NexmoClient
arguments: ["%jhg_nexmo.api_key%","%jhg_nexmo.api_secret%"]
jhg_nexmo.sms.sender:
class: Jhg\NexmoBundle\Sender\SmsSender
arguments: [@service_container,@jhg_nexmo.sms_message]
jhg_nexmo_account:
class: Jhg\NexmoBundle\Managers\AccountManager
arguments: [@jhg_nexmo_client]
jhg_nexmo_sms:
class: Jhg\NexmoBundle\Managers\SmsManager
arguments: [@jhg_nexmo_client,"%jhg_nexmo.from_name%"]
jhg_nexmo_number:
class: Jhg\NexmoBundle\Managers\NumberManager
arguments: [@jhg_nexmo_client]

View File

@ -1,49 +0,0 @@
<?php
namespace Jhg\NexmoBundle\Sender;
use Nexmo\NexmoMessage;
use Symfony\Component\DependencyInjection\Container;
/**
* Class SmsSender
* @package Jhg\NexmoBundle\Sender
*
* @author Javi Hernández
*/
class SmsSender
{
/**
* @var \Nexmo\NexmoMessage
*/
protected $nexmoMessage;
/**
* @var \Symfony\Component\DependencyInjection\Container
*/
protected $container;
/**
* @param Container $container
* @param NexmoMessage $nexmoMessage
*/
public function __construct(Container $container,NexmoMessage $nexmoMessage) {
$this->container = $container;
$this->nexmoMessage = $nexmoMessage;
}
/**
* @param $number
* @param null $fromName
* @param $message
* @param null $unicode
* @param int $status_report_req
* @return array|bool|\Nexmo\stdClass
*/
public function send($number,$fromName=null,$message,$unicode=null, $status_report_req=0) {
if($fromName===null)
$fromName = $this->container->getParameter('jhg_nexmo.from_name');
return $this->nexmoMessage->sendText($number,$fromName,$message,$unicode,$status_report_req);
}
}

View File

@ -1,46 +0,0 @@
<?php
namespace Jhg\NexmoBundle\Tests\Command;
use Jhg\NexmoBundle\Command\SmsSendCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
* Class SmsSendCommandTest
* @package Jhg\NexmoBundle\Tests\Command
*
* @author Javi Hernández
*/
class SmsSendCommandTest extends \PHPUnit_Framework_TestCase {
public function testExecute()
{
$application = new Application();
$application->add(new SmsSendCommand());
$command = $application->find('nexmo:sms:send');
$commandTester = new CommandTester($command);
try {
$commandTester->execute(array());
$this->assertTrue(false);
} catch(\RuntimeException $e) {
$this->assertEquals('Not enough arguments.',$e->getMessage());
}
$arguments = array(
'command' => 'nexmo:sms:send',
'number' => 'demo:greet',
'fromName' => 'Fabien',
'message' => '',
);
$input = new ArrayInput($arguments);
$returnCode = $command->run($input, $output);
$commandTester->execute(array("+34666555444","MyApp","Hello World!!"));
// $this->assertRegExp('/.../', $commandTester->getDisplay());
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace Jhg\NexmoBundle\Tests\Sender;
use Jhg\NexmoBundle\Sender\SmsSender;
/**
* Class SmsSenderTest
* @package Jhg\NexmoBundle\Tests\Sender
*
* @author Javi Hernández
*/
class SmsSenderTest extends \PHPUnit_Framework_TestCase
{
public function getSendData() {
return array(
array(
array(
'jhg_nexmo.api_key'=>'asdfghjkl',
'jhg_nexmo.api_secret'=>'1234567890',
'jhg_nexmo.from_name'=>'From Name',
),
'+34666555444',
'From Name',
'Message',
null,
0,
array(),
),
);
}
/**
* @dataProvider getSendData
*/
public function testSend($containerParameters,$number,$fromName,$message,$unicode,$status_report_req,$sendRequestReturnData) {
$containerMock = $this->getMock('Symfony\Component\DependencyInjection\Container');
$nexmoMessageMock = $this->getMock('Nexmo\NexmoMessage',array(),array($containerMock,$containerParameters['jhg_nexmo.api_key'],$containerParameters['jhg_nexmo.api_secret']));
$nexmoMessageMock->expects($this->any())
->method('sendRequest')
->will($this->returnValue($sendRequestReturnData));
$sender = new SmsSender($containerMock,$nexmoMessageMock);
$result = $sender->send($number,$fromName,$message,$unicode,$status_report_req);
$this->assertEquals(null,$result);
}
}

17
Utils/PhoneNumber.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace Jhg\NexmoBundle\Utils;
/**
* Class PhoneNumber
* @package Jhg\NexmoBundle\Utils
* @Author Javi Hernández
*/
class PhoneNumber {
/**
* @param $number
* @return mixed
*/
public static function prefixFilter($number) {
return str_ireplace('+','00',$number);
}
}