From cae1a2f0710a02e6e8a3f78280123b7283426603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Hern=C3=A1ndez=20Gil?= Date: Mon, 31 Mar 2014 00:20:02 +0200 Subject: [PATCH] New implementation of bundle --- Command/AccountBalanceCommand.php | 34 +++++ Command/SmsPricingCommand.php | 44 ++++++ Command/SmsSendCommand.php | 29 ++-- DependencyInjection/Configuration.php | 28 ++-- DependencyInjection/JhgNexmoExtension.php | 30 +--- JhgNexmoBundle.php | 2 +- Managers/AccountManager.php | 87 ++++++++++++ Managers/NumberManager.php | 40 ++++++ Managers/SmsManager.php | 67 +++++++++ Model/Sms.php | 11 ++ Model/SmsSendResponse.php | 158 ++++++++++++++++++++++ NexmoClient/NexmoClient.php | 107 +++++++++++++++ Resources/config/services.yml | 20 ++- Sender/SmsSender.php | 49 ------- Tests/Command/SmsSendCommandTest.php | 46 ------- Tests/Sender/SmsSenderTest.php | 49 ------- Utils/PhoneNumber.php | 17 +++ 17 files changed, 619 insertions(+), 199 deletions(-) create mode 100644 Command/AccountBalanceCommand.php create mode 100644 Command/SmsPricingCommand.php create mode 100644 Managers/AccountManager.php create mode 100644 Managers/NumberManager.php create mode 100644 Managers/SmsManager.php create mode 100644 Model/Sms.php create mode 100644 Model/SmsSendResponse.php create mode 100644 NexmoClient/NexmoClient.php delete mode 100644 Sender/SmsSender.php delete mode 100644 Tests/Command/SmsSendCommandTest.php delete mode 100644 Tests/Sender/SmsSenderTest.php create mode 100644 Utils/PhoneNumber.php diff --git a/Command/AccountBalanceCommand.php b/Command/AccountBalanceCommand.php new file mode 100644 index 0000000..2bd3f3d --- /dev/null +++ b/Command/AccountBalanceCommand.php @@ -0,0 +1,34 @@ +setName('nexmo:account:balance') + ->setDescription('Gets account balance') + ->setHelp("The nexmo:account:balance 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)); + } + +} diff --git a/Command/SmsPricingCommand.php b/Command/SmsPricingCommand.php new file mode 100644 index 0000000..ba936d2 --- /dev/null +++ b/Command/SmsPricingCommand.php @@ -0,0 +1,44 @@ +setName('nexmo:sms:pricing') + ->setDescription('Gets sms price for given country') + ->setDefinition(array( + new InputArgument('country', InputArgument::REQUIRED, 'The country code'), + )) + ->setHelp("The nexmo:sms:pricing 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)); + } + } + +} diff --git a/Command/SmsSendCommand.php b/Command/SmsSendCommand.php index ff7b734..9636508 100644 --- a/Command/SmsSendCommand.php +++ b/Command/SmsSendCommand.php @@ -8,15 +8,14 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** - * @author Javi Hernández Gil + * @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,10 +23,11 @@ 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(<<nexmo:sms:send command sends a SMS message through Nexmo API - php app/console nexmo:sms:send +34666555444 MyApp "Hello World!!" +php app/console nexmo:sms:send +34666555444 MyApp "Hello World!!" EOT ); } @@ -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'); - - $sender = $this->getContainer()->get('jhg_nexmo.sms.sender'); - - if($sender->send($number,$fromName,$message,null,0)) { - $output->writeln(sprintf('SMS send to %u from %s: "%s"',$number,$fromName,$message)); + $report = (int)$input->getOption('report'); + + $smsManager = $this->getContainer()->get('jhg_nexmo_sms'); + + 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)); } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 259a496..369eb3b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -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() ; diff --git a/DependencyInjection/JhgNexmoExtension.php b/DependencyInjection/JhgNexmoExtension.php index d5f5b4e..893447c 100644 --- a/DependencyInjection/JhgNexmoExtension.php +++ b/DependencyInjection/JhgNexmoExtension.php @@ -21,35 +21,11 @@ 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'); - } - + $container->setParameter('jhg_nexmo.disable_delivery', $config['disable_delivery']); + $container->setParameter('jhg_nexmo.from_name', $config['from_name']); + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); } diff --git a/JhgNexmoBundle.php b/JhgNexmoBundle.php index 89fafaa..df93148 100644 --- a/JhgNexmoBundle.php +++ b/JhgNexmoBundle.php @@ -5,7 +5,7 @@ use Symfony\Component\HttpKernel\Bundle\Bundle; /** * Bundle. - * @author Javi Hernández Gil + * @author Javi Hernández */ class JhgNexmoBundle extends Bundle { diff --git a/Managers/AccountManager.php b/Managers/AccountManager.php new file mode 100644 index 0000000..a0490e4 --- /dev/null +++ b/Managers/AccountManager.php @@ -0,0 +1,87 @@ +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'); + } +} \ No newline at end of file diff --git a/Managers/NumberManager.php b/Managers/NumberManager.php new file mode 100644 index 0000000..186eb8d --- /dev/null +++ b/Managers/NumberManager.php @@ -0,0 +1,40 @@ +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'); + } +} \ No newline at end of file diff --git a/Managers/SmsManager.php b/Managers/SmsManager.php new file mode 100644 index 0000000..2ae3152 --- /dev/null +++ b/Managers/SmsManager.php @@ -0,0 +1,67 @@ +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'); + } +} \ No newline at end of file diff --git a/Model/Sms.php b/Model/Sms.php new file mode 100644 index 0000000..87ebb0e --- /dev/null +++ b/Model/Sms.php @@ -0,0 +1,11 @@ +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; + } + + +} \ No newline at end of file diff --git a/NexmoClient/NexmoClient.php b/NexmoClient/NexmoClient.php new file mode 100644 index 0000000..9012c53 --- /dev/null +++ b/NexmoClient/NexmoClient.php @@ -0,0 +1,107 @@ +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]; + } +} \ No newline at end of file diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 2ffbdf8..6d013a6 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -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] \ No newline at end of file + 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] \ No newline at end of file diff --git a/Sender/SmsSender.php b/Sender/SmsSender.php deleted file mode 100644 index db68353..0000000 --- a/Sender/SmsSender.php +++ /dev/null @@ -1,49 +0,0 @@ -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); - } -} diff --git a/Tests/Command/SmsSendCommandTest.php b/Tests/Command/SmsSendCommandTest.php deleted file mode 100644 index 8986380..0000000 --- a/Tests/Command/SmsSendCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -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()); - } -} \ No newline at end of file diff --git a/Tests/Sender/SmsSenderTest.php b/Tests/Sender/SmsSenderTest.php deleted file mode 100644 index 41462e7..0000000 --- a/Tests/Sender/SmsSenderTest.php +++ /dev/null @@ -1,49 +0,0 @@ -'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); - } -} \ No newline at end of file diff --git a/Utils/PhoneNumber.php b/Utils/PhoneNumber.php new file mode 100644 index 0000000..518f982 --- /dev/null +++ b/Utils/PhoneNumber.php @@ -0,0 +1,17 @@ +