Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Utopia\Messaging\Messages\SMS;
use Utopia\Messaging\Response;

class Vonage extends SMSAdapter
class VonageLegacy extends SMSAdapter

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Breaking rename with no backward-compatibility alias

Renaming VonageVonageLegacy drops the original class name entirely. Any downstream code using new \Utopia\Messaging\Adapter\SMS\Vonage(...) or use Utopia\Messaging\Adapter\SMS\Vonage will throw a fatal Class not found error after upgrading this library. The PR description mentions a "backward compat note" but no alias or deprecation shim is present in the diff. Consider adding class_alias(VonageLegacy::class, 'Utopia\\Messaging\\Adapter\\SMS\\Vonage'); at the bottom of this file (or in a dedicated compat file), or coordinate a semver-major release.

{
protected const NAME = 'Vonage';
protected const NAME = 'Vonage Legacy';

/**
* @param string $apiKey Vonage API Key
Expand All @@ -22,7 +22,6 @@ public function __construct(
private string $apiSecret,
private ?string $from = null
) {
parent::__construct();
}

public function getName(): string
Expand Down
102 changes: 102 additions & 0 deletions src/Utopia/Messaging/Adapter/SMS/VonageMessages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace Utopia\Messaging\Adapter\SMS;

use Utopia\Messaging\Adapter\SMS as SMSAdapter;
use Utopia\Messaging\Messages\SMS as SMSMessage;
use Utopia\Messaging\Response;

class VonageMessages extends SMSAdapter
{
protected const NAME = 'Vonage Messages';

public function __construct(
private string $apiKey,
private string $apiSecret,
private ?string $from = null
) {
}

protected function getApiEndpoint(): string
{
return 'https://api.vonage.com/v1/messages';
}

protected function getAuthorizationHeader(): string
{
return 'Basic ' . \base64_encode("{$this->apiKey}:{$this->apiSecret}");
}

/**
* @return array<string>
*/
protected function getRequestHeaders(): array
{
return [
"Authorization: {$this->getAuthorizationHeader()}",
'Content-Type: application/json',
'Accept: application/json',
'User-Agent: Utopia Messaging',
];
}

public function getName(): string
{
return static::NAME;
}

public function getMaxMessagesPerRequest(): int
{
return 1;
}

protected function process(SMSMessage $message): array
{
$to = \ltrim($message->getTo()[0], '+');
$from = $this->from ?? $message->getFrom();
$from = $from !== null ? \ltrim($from, '+') : null;

$response = new Response($this->getType());

if (empty($from)) {
$response->addResult($message->getTo()[0], 'The "from" field is required for the Vonage Messages API.');
return $response->toArray();
}

$result = $this->request(
method: 'POST',
url: $this->getApiEndpoint(),
headers: $this->getRequestHeaders(),
body: [
'message_type' => 'text',
'to' => $to,
'from' => $from,
'text' => $message->getContent(),
'channel' => 'sms',
],
);

if ($result['statusCode'] === 202) {
$response->setDeliveredTo(1);
$response->addResult($message->getTo()[0]);
} else {
$errorMessage = "Error {$result['statusCode']}";

if (\is_array($result['response'])) {
if (isset($result['response']['detail'])) {
$errorMessage = $result['response']['detail'];
} elseif (isset($result['response']['title'])) {
$errorMessage = $result['response']['title'];
}
} elseif (!empty($result['error'])) {
$errorMessage = $result['error'];
} elseif (\is_string($result['response']) && !empty($result['response'])) {
$errorMessage = "Error {$result['statusCode']}: " . \mb_strimwidth(\strip_tags($result['response']), 0, 100, '...');
}

$response->addResult($message->getTo()[0], $errorMessage);
}

return $response->toArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Utopia\Tests\Adapter\Base;

class VonageTest extends Base
class VonageLegacyTest extends Base
{
/**
* @throws \Exception
Expand Down
36 changes: 36 additions & 0 deletions tests/Messaging/Adapter/SMS/VonageMessagesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Utopia\Tests\Adapter\SMS;

use Utopia\Messaging\Adapter\SMS\VonageMessages;
use Utopia\Messaging\Messages\SMS;
use Utopia\Tests\Adapter\Base;

class VonageMessagesTest extends Base
{
/**
* @throws \Exception
*/
public function testSendSMS(): void
{
$this->markTestSkipped('Vonage Messages credentials are not available.');

/*
$apiKey = \getenv('VONAGE_MESSAGES_API_KEY');
$apiSecret = \getenv('VONAGE_MESSAGES_API_SECRET');

$sender = new VonageMessages($apiKey, $apiSecret);

$message = new SMS(
to: [\getenv('VONAGE_MESSAGES_TO')],
content: 'Test Content',
from: \getenv('VONAGE_MESSAGES_FROM'),
);

$response = $sender->send($message);

$this->assertNotEmpty($response['results']);
$this->assertNotEmpty($response['results'][0]['success']);
*/
}
Comment on lines +14 to +35

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Entire test body is dead code

The test immediately calls markTestSkipped and the real assertions are in a block comment rather than an if (! $creds) { markTestSkipped(); } guard pattern. Tests for other adapters (e.g., VonageLegacyTest) use live credentials gated by env vars. If VONAGE_MESSAGES_API_KEY etc. are simply not available in CI, the existing skip-unless-env-var pattern should be used here too, so the test actually executes when credentials are configured.

}