diff --git a/src/Utopia/Messaging/Adapters/SMS/GEOSMS.php b/src/Utopia/Messaging/Adapters/SMS/GEOSMS.php index 190db71a..da2e4ed8 100644 --- a/src/Utopia/Messaging/Adapters/SMS/GEOSMS.php +++ b/src/Utopia/Messaging/Adapters/SMS/GEOSMS.php @@ -34,45 +34,77 @@ public function setLocal(string $callingCode, SMSAdapter $adapter): self return $this; } + protected function filterCallingCodesByAdapter(SMSAdapter $adapter): array + { + $result = []; + + foreach ($this->localAdapters as $callingCode => $localAdapter) { + if ($localAdapter === $adapter) { + $result[] = $callingCode; + } + } + + return $result; + } + protected function process(SMS $message): string { - $recipientsByCallingCode = $this->groupRecipientsByCallingCode($message->getTo()); - $responses = []; - $errors = []; + $results = []; + $recipients = $message->getTo(); - foreach ($recipientsByCallingCode as $callingCode => $recipients) { - $adapter = isset($this->localAdapters[$callingCode]) - ? $this->localAdapters[$callingCode] - : $this->defaultAdapter; + do { + [$nextRecipients, $nextAdapter] = $this->getNextRecipientsAndAdapter($recipients); try { - $responses[] = $adapter->send(new SMS( - to: $recipients, - content: $message->getContent(), - from: $message->getFrom(), - attachments: $message->getAttachments() + $results[$nextAdapter->getName()] = json_decode($nextAdapter->send( + new SMS( + to: $nextRecipients, + content: $message->getContent(), + from: $message->getFrom(), + attachments: $message->getAttachments() + ) )); } catch (\Exception $e) { - $errors[] = $e; + $results[$nextAdapter->getName()] = [ + 'type' => 'error', + 'message' => $e->getMessage(), + ]; } - } - if (count($errors) > 0) { - throw new \Exception('Failed to send SMS to some recipients', 0, $errors[0]); - } + $recipients = \array_diff($recipients, $nextRecipients); + } while (count($recipients) > 0); - return $responses[0]; + return \json_encode($results); } - protected function groupRecipientsByCallingCode(array $recipients): array + protected function getNextRecipientsAndAdapter(array $recipients): array { - $result = []; + $nextRecipients = []; + $nextAdapter = null; foreach ($recipients as $recipient) { - $callingCode = CallingCode::fromPhoneNumber($recipient); - $result[$callingCode][] = $recipient; + $adapter = $this->getAdapterByPhoneNumber($recipient); + + if ($nextAdapter === null || $adapter === $nextAdapter) { + $nextAdapter = $adapter; + $nextRecipients[] = $recipient; + } } - return $result; + return [$nextRecipients, $nextAdapter]; + } + + protected function getAdapterByPhoneNumber(?string $phoneNumber): SMSAdapter + { + $callingCode = CallingCode::fromPhoneNumber($phoneNumber); + if ($callingCode === null || empty($callingCode)) { + return $this->defaultAdapter; + } + + if (isset($this->localAdapters[$callingCode])) { + return $this->localAdapters[$callingCode]; + } + + return $this->defaultAdapter; } } diff --git a/src/Utopia/Messaging/Adapters/SMS/GEOSMS/CallingCode.php b/src/Utopia/Messaging/Adapters/SMS/GEOSMS/CallingCode.php index 836264bd..141c9250 100644 --- a/src/Utopia/Messaging/Adapters/SMS/GEOSMS/CallingCode.php +++ b/src/Utopia/Messaging/Adapters/SMS/GEOSMS/CallingCode.php @@ -265,6 +265,8 @@ class CallingCode public const NORFOLK_ISLANDS = '672'; + public const NORTH_AMERICA = '1'; + public const NORTHERN_MARIANA_ISLANDS = '670'; public const NORWAY = '47'; @@ -365,8 +367,6 @@ class CallingCode public const URUGUAY = '598'; - public const NORTH_AMERICA = '1'; - public const VANUATU = '678'; public const VENEZUELA = '58'; diff --git a/tests/e2e/SMS/GEOSMS/CallingCodeTest.php b/tests/e2e/SMS/GEOSMS/CallingCodeTest.php index 74f4a60b..7a460d83 100644 --- a/tests/e2e/SMS/GEOSMS/CallingCodeTest.php +++ b/tests/e2e/SMS/GEOSMS/CallingCodeTest.php @@ -10,7 +10,9 @@ public function testFromPhoneNumber() { $this->assertEquals(CallingCode::NORTH_AMERICA, CallingCode::fromPhoneNumber('+11234567890')); $this->assertEquals(CallingCode::INDIA, CallingCode::fromPhoneNumber('+911234567890')); + $this->assertEquals(CallingCode::ISRAEL, CallingCode::fromPhoneNumber('9721234567890')); $this->assertEquals(CallingCode::UNITED_ARAB_EMIRATES, CallingCode::fromPhoneNumber('009711234567890')); $this->assertEquals(CallingCode::UNITED_KINGDOM, CallingCode::fromPhoneNumber('011441234567890')); + $this->assertEquals(null, CallingCode::fromPhoneNumber('2')); } } diff --git a/tests/e2e/SMS/GEOSMSTest.php b/tests/e2e/SMS/GEOSMSTest.php index 26b0794e..d635ca45 100644 --- a/tests/e2e/SMS/GEOSMSTest.php +++ b/tests/e2e/SMS/GEOSMSTest.php @@ -12,8 +12,8 @@ class GEOSMSTest extends Base public function testSendSMSUsingDefaultAdapter() { $defaultAdapterMock = $this->createMock(SMSAdapter::class); - $defaultAdapterMock->method('getMaxMessagesPerRequest') - ->willReturn(1); + $defaultAdapterMock->method('getName') + ->willReturn('default'); $defaultAdapterMock->method('send') ->willReturn(json_encode(['status' => 'success'])); @@ -28,26 +28,85 @@ public function testSendSMSUsingDefaultAdapter() from: $from ); - $result = json_decode($adapter->send($message)); + $result = json_decode($adapter->send($message), true); - $this->assertEquals('success', $result->status); + $this->assertEquals(1, count($result)); + $this->assertEquals('success', $result['default']['status']); } public function testSendSMSUsingLocalAdapter() { $defaultAdapterMock = $this->createMock(SMSAdapter::class); - $defaultAdapterMock->method('getMaxMessagesPerRequest') - ->willReturn(1); $localAdapterMock = $this->createMock(SMSAdapter::class); - $localAdapterMock->method('getMaxMessagesPerRequest') - ->willReturn(1); + $localAdapterMock->method('getName') + ->willReturn('local'); $localAdapterMock->method('send') - ->willReturn(json_encode(['status' => 'success', 'adapter' => 'local'])); + ->willReturn(json_encode(['status' => 'success'])); + + $adapter = new GEOSMS($defaultAdapterMock); + $adapter->setLocal(CallingCode::INDIA, $localAdapterMock); + + $to = ['+911234567890']; + $from = 'Sender'; + + $message = new SMS( + to: $to, + content: 'Test Content', + from: $from + ); + + $result = json_decode($adapter->send($message), true); + + $this->assertEquals(1, count($result)); + $this->assertEquals('success', $result['local']['status']); + } + + public function testSendSMSUsingLocalAdapterAndDefault() + { + $defaultAdapterMock = $this->createMock(SMSAdapter::class); + $defaultAdapterMock->method('getName') + ->willReturn('default'); + $defaultAdapterMock->method('send') + ->willReturn(json_encode(['status' => 'success'])); + $localAdapterMock = $this->createMock(SMSAdapter::class); + $localAdapterMock->method('getName') + ->willReturn('local'); + $localAdapterMock->method('send') + ->willReturn(json_encode(['status' => 'success'])); + + $adapter = new GEOSMS($defaultAdapterMock); + $adapter->setLocal(CallingCode::INDIA, $localAdapterMock); + + $to = ['+911234567890', '+11234567890']; + $from = 'Sender'; + + $message = new SMS( + to: $to, + content: 'Test Content', + from: $from + ); + + $result = json_decode($adapter->send($message), true); + + $this->assertEquals(2, count($result)); + $this->assertEquals('success', $result['local']['status']); + $this->assertEquals('success', $result['default']['status']); + } + + public function testSendSMSUsingGroupedLocalAdapter() + { + $defaultAdapterMock = $this->createMock(SMSAdapter::class); + $localAdapterMock = $this->createMock(SMSAdapter::class); + $localAdapterMock->method('getName') + ->willReturn('local'); + $localAdapterMock->method('send') + ->willReturn(json_encode(['status' => 'success'])); $adapter = new GEOSMS($defaultAdapterMock); - $adapter->setLocal(CallingCode::UNITED_KINGDOM, $localAdapterMock); + $adapter->setLocal(CallingCode::INDIA, $localAdapterMock); + $adapter->setLocal(CallingCode::NORTH_AMERICA, $localAdapterMock); - $to = ['+441234567890']; + $to = ['+911234567890', '+11234567890']; $from = 'Sender'; $message = new SMS( @@ -56,9 +115,9 @@ public function testSendSMSUsingLocalAdapter() from: $from ); - $result = json_decode($adapter->send($message)); + $result = json_decode($adapter->send($message), true); - $this->assertEquals('success', $result->status); - $this->assertEquals('local', $result->adapter); + $this->assertEquals(1, count($result)); + $this->assertEquals('success', $result['local']['status']); } }