Skip to content

feat: add Vonage multi-channel Messages adapter (#8139)#110

Open
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:feat/8139-vonage-messages-adapter
Open

feat: add Vonage multi-channel Messages adapter (#8139)#110
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:feat/8139-vonage-messages-adapter

Conversation

@deepshekhardas

@deepshekhardas deepshekhardas commented Mar 16, 2026

Copy link
Copy Markdown

Adds a Vonage multi-channel Messages adapter supporting SMS, WhatsApp, Viber, and MMS.

Changes

  • New \VonageTrait\ with shared HTTP transport (\src/Utopia/Messaging/Adapter/VonageTrait.php)
  • New SMS adapter: \VonageMessages\ (\src/Utopia/Messaging/Adapter/SMS/VonageMessages.php)
  • New WhatsApp adapter: \Vonage\ (\src/Utopia/Messaging/Adapter/WhatsApp/Vonage.php)
  • New Viber adapter: \Vonage\ (\src/Utopia/Messaging/Adapter/Viber/Vonage.php)
  • New MMS adapter: \Vonage\ (\src/Utopia/Messaging/Adapter/MMS/Vonage.php)
  • Abstract \MMS\ adapter class (\src/Utopia/Messaging/Adapter/MMS.php)
  • Abstract \Viber\ adapter class (\src/Utopia/Messaging/Adapter/Viber.php)
  • Abstract \WhatsApp\ adapter class (\src/Utopia/Messaging/Adapter/WhatsApp.php)
  • Tests for the Vonage Messages SMS adapter

Testing

  • Unit tests added for VonageMessages adapter
  • Follows existing adapter patterns (Telnyx, Mock, etc.)

Closes #8139

@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@deepshekhardas has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 29 minutes and 8 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a7c8cc9-e56f-4b5b-b848-bdb8839131d9

📥 Commits

Reviewing files that changed from the base of the PR and between fcb4c3c and ba22934.

📒 Files selected for processing (9)
  • src/Utopia/Messaging/Adapter/MMS.php
  • src/Utopia/Messaging/Adapter/MMS/Vonage.php
  • src/Utopia/Messaging/Adapter/SMS/VonageMessages.php
  • src/Utopia/Messaging/Adapter/Viber.php
  • src/Utopia/Messaging/Adapter/Viber/Vonage.php
  • src/Utopia/Messaging/Adapter/VonageTrait.php
  • src/Utopia/Messaging/Adapter/WhatsApp.php
  • src/Utopia/Messaging/Adapter/WhatsApp/Vonage.php
  • tests/Messaging/Adapter/SMS/VonageMessagesTest.php
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can generate walkthrough in a markdown collapsible section to save space.

Enable the reviews.collapse_walkthrough setting to generate walkthrough in a markdown collapsible section.

@deepshekhardas deepshekhardas force-pushed the feat/8139-vonage-messages-adapter branch from ba22934 to d623ac7 Compare June 2, 2026 01:54
@deepshekhardas

Copy link
Copy Markdown
Author

Rebased onto latest main. Could you please review? 🙏

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces a VonageTrait shared transport and four concrete adapters (SMS, WhatsApp, Viber, MMS) that use the Vonage Messages API (/v1/messages) with JWT authentication, plus abstract base classes for the new channel types.

  • VonageTrait centralises JWT generation and the HTTP call, but its constructor never calls parent::__construct(), leaving Adapter::$sendCounter uninitialised and causing a fatal error on every send() call.
  • SMS/VonageMessages.php uses a bare use VonageTrait; without the fully-qualified namespace, making the trait unresolvable at runtime.
  • Viber/Vonage.php passes "viber" as the channel, but the Vonage Messages API requires "viber_service"; every Viber send will be rejected with a 422.
  • Tests will throw before reaching any assertion: setEndpoint() is undefined on all four adapters, send() returns an array (not a string) so json_decode will receive the wrong type, and the placeholder RSA key is not a valid key so openssl_sign will throw during JWT creation.

Confidence Score: 2/5

Not safe to merge — every adapter crashes at runtime before sending a message, and the Viber channel name is wrong at the API level.

The trait constructor silently skips initialising $sendCounter, so any call to send() will throw a typed-property-not-initialised fatal. The SMS adapter's bare use VonageTrait; resolves to a non-existent class in its namespace, causing a class-not-found error at instantiation. The Viber adapter sends an invalid channel value that the Vonage API rejects. The test suite cannot execute any assertions: it calls an undefined method, passes an array to json_decode, and uses a non-functional RSA key.

src/Utopia/Messaging/Adapter/VonageTrait.php, src/Utopia/Messaging/Adapter/SMS/VonageMessages.php, src/Utopia/Messaging/Adapter/Viber/Vonage.php, and tests/Messaging/Adapter/SMS/VonageMessagesTest.php

Important Files Changed

Filename Overview
src/Utopia/Messaging/Adapter/VonageTrait.php Core shared transport for all Vonage adapters; missing parent::__construct() call leaves $sendCounter uninitialized, and two separate time() calls for iat/exp create a race
src/Utopia/Messaging/Adapter/SMS/VonageMessages.php SMS adapter missing the VonageTrait namespace import (uses bare use VonageTrait;) causing a fatal class-not-found error; JWT and Response imports are unused
src/Utopia/Messaging/Adapter/Viber/Vonage.php Passes channel "viber" to the Vonage Messages API which requires "viber_service"; all Viber sends will be rejected by the API
src/Utopia/Messaging/Adapter/WhatsApp/Vonage.php WhatsApp adapter delegating to VonageTrait with correct "whatsapp" channel value; inherits trait constructor issue
src/Utopia/Messaging/Adapter/MMS/Vonage.php MMS adapter delegating to VonageTrait with correct "mms" channel value; inherits trait constructor issue
src/Utopia/Messaging/Adapter/MMS.php New abstract MMS base class following the pattern of SMS.php; reuses SMSMessage as message type which is a reasonable simplification
src/Utopia/Messaging/Adapter/Viber.php New abstract Viber base class following existing SMS.php pattern
src/Utopia/Messaging/Adapter/WhatsApp.php New abstract WhatsApp base class following existing SMS.php pattern
tests/Messaging/Adapter/SMS/VonageMessagesTest.php Tests will throw before any assertions: setEndpoint() is not defined on any Vonage adapter, send() returns array not string so json_decode will fail, and the fake RSA key will cause openssl_sign to throw during JWT creation

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/Utopia/Messaging/Adapter/Viber/Vonage.php:30
**Wrong Viber channel value — all Viber sends will fail**

The Vonage Messages API requires `"viber_service"` as the channel value for Viber messages, not `"viber"`. Sending `channel: "viber"` will result in a 422 Unprocessable Entity from Vonage and no message will be delivered. This is confirmed by the [Vonage API reference](https://developer.vonage.com/en/messages/code-snippets/viber/send-text) and the [OpenAPI spec](https://raw.githubusercontent.com/api-evangelist/vonage/refs/heads/main/openapi/vonage-openapi.yml).

```suggestion
        return $this->processMessage($message, 'viber_service');
```

Reviews (3): Last reviewed commit: "feat: add Vonage multi-channel Messages ..." | Re-trigger Greptile

Comment thread src/Utopia/Messaging/Adapter/SMS/VonageMessages.php
Comment on lines +37 to +45

// This assumes the mock server returns a success response
// In a real environment, we'd check the request-catcher data
$this->assertNotEmpty($result);
}

public function testSendWhatsApp(): void
{
$sender = new VonageWhatsApp($this->applicationId, $this->privateKey);

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 setEndpoint() is undefined on these adapters. None of VonageMessages, VonageWhatsApp, VonageViber, or VonageMMS define a setEndpoint() method — it only exists on Mock. Every test method in this class will crash with "Call to undefined method" before reaching any assertions. The same issue occurs in all four test methods (testSendSMS, testSendWhatsApp, testSendViber, testSendMMS). Either add setEndpoint() / getEndpoint() to VonageTrait, or use an environment variable / constructor parameter to override the base URL.

Comment on lines +43 to +45
public function testSendWhatsApp(): void
{
$sender = new VonageWhatsApp($this->applicationId, $this->privateKey);

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 json_decode receives an array, not a string. send() returns array, so \json_decode($response, true) is called with an array argument. In PHP 8 this causes a TypeError; in earlier versions it silently returns null, making assertNotEmpty($result) fail. The same pattern is repeated in all four test methods. Either call send() directly and use assertResponse() (already defined in Base), or cast to JSON before decoding.

Comment thread src/Utopia/Messaging/Adapter/VonageTrait.php
@deepshekhardas deepshekhardas force-pushed the feat/8139-vonage-messages-adapter branch from d623ac7 to 4448c62 Compare June 17, 2026 09:19
Comment on lines +16 to +21
public function __construct(
private string $applicationId,
private string $privateKey,
private ?string $from = null
) {
}

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 Initialize base telemetry. The trait constructor replaces Adapter::__construct() but never calls it, so the base class private $sendCounter property stays uninitialized. A normal new VonageMessages(...)->send($message) reaches Adapter::recordResponse() after process() returns, then reads $this->sendCounter and fails with Typed property Utopia\Messaging\Adapter::$sendCounter must not be accessed before initialization unless callers manually call setTelemetry() first.

Artifacts

Repro: focused PHP harness for VonageMessages send without initialized telemetry

  • Contains supporting evidence from the run (text/x-php; charset=utf-8).

Stack trace captured during the T-Rex run

  • Keeps the raw stack trace available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +51 to +55
headers: [
'Content-Type: application/json',
'Authorization: Bearer ' . $jwt,
'Accept: application/json',
],

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 Use header strings. Adapter::request() expects headers as a list of complete header strings and passes the same array to CURLOPT_HTTPHEADER. This associative array can send malformed headers to cURL, so the Vonage request may miss a valid Authorization or Content-Type header.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@deepshekhardas deepshekhardas force-pushed the feat/8139-vonage-messages-adapter branch from 4448c62 to 43b104a Compare July 10, 2026 05:01
* @throws \Exception
*/
protected function process(SMSMessage $message): array
{

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 Wrong Viber channel value — all Viber sends will fail

The Vonage Messages API requires "viber_service" as the channel value for Viber messages, not "viber". Sending channel: "viber" will result in a 422 Unprocessable Entity from Vonage and no message will be delivered. This is confirmed by the Vonage API reference and the OpenAPI spec.

Suggested change
{
return $this->processMessage($message, 'viber_service');
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Viber/Vonage.php
Line: 30

Comment:
**Wrong Viber channel value — all Viber sends will fail**

The Vonage Messages API requires `"viber_service"` as the channel value for Viber messages, not `"viber"`. Sending `channel: "viber"` will result in a 422 Unprocessable Entity from Vonage and no message will be delivered. This is confirmed by the [Vonage API reference](https://developer.vonage.com/en/messages/code-snippets/viber/send-text) and the [OpenAPI spec](https://raw.githubusercontent.com/api-evangelist/vonage/refs/heads/main/openapi/vonage-openapi.yml).

```suggestion
        return $this->processMessage($message, 'viber_service');
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant