Skip to content

feat: add Telnyx SMS adapter (#7811)#108

Open
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:feat/7811-telnyx-adapter
Open

feat: add Telnyx SMS adapter (#7811)#108
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:feat/7811-telnyx-adapter

Conversation

@deepshekhardas
Copy link
Copy Markdown

@deepshekhardas deepshekhardas commented Mar 16, 2026

Updates the Telnyx SMS adapter.

Changes

  • Refactored \Telnyx.php\ SMS adapter
  • Updated corresponding tests

Testing

  • Tests updated to match refactored adapter

Closes #7811

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 16, 2026

Walkthrough

The Telnyx SMS adapter's constructor parameter $from was changed from nullable to required non-nullable. The process() method was refactored to send individual API requests for each recipient instead of a single batch request. Request construction now uses string interpolation for headers and includes per-recipient values for from, to, and text fields. Delivery reporting and error handling were updated to track metrics per recipient. The corresponding test file was updated to enable the Telnyx sender, construct actual SMS objects using environment variables, and execute assertions instead of placeholder code.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'feat: add Telnyx SMS adapter' clearly and accurately summarizes the main change: adding a new SMS adapter for the Telnyx service provider.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description accurately describes the changeset: refactored Telnyx SMS adapter and updated corresponding tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Messaging/Adapter/SMS/TelnyxTest.php`:
- Around line 16-22: The test fails because Telnyx is instantiated with too few
arguments and doesn't guard missing env vars; update the test in TelnyxTest to
first read and validate getenv('TELNYX_API_KEY'), getenv('TELNYX_TO') and
getenv('TELNYX_FROM') and call markTestSkipped (or return) if any are empty,
then construct the Telnyx sender passing the required from argument (e.g.,
include the from value when calling new Telnyx(...)) and create the SMS using
the validated env values before calling send.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea64e157-ee53-4fa5-baaf-f297eaae6463

📥 Commits

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

📒 Files selected for processing (2)
  • src/Utopia/Messaging/Adapter/SMS/Telnyx.php
  • tests/Messaging/Adapter/SMS/TelnyxTest.php

Comment on lines +16 to +22
$sender = new Telnyx(\getenv('TELNYX_API_KEY'));

// $message = new SMS(
// to: ['+18034041123'],
// content: 'Test Content',
// from: '+15005550006'
// );
$message = new SMS(
to: [\getenv('TELNYX_TO')],
content: 'Test Content',
from: \getenv('TELNYX_FROM')
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Pass the required Telnyx from argument and guard missing env vars.

This test currently instantiates Telnyx with too few arguments, which will fail before the send call runs. It also should skip cleanly when Telnyx env vars are not configured.

Proposed patch
 public function testSendSMS(): void
 {
-    $sender = new Telnyx(\getenv('TELNYX_API_KEY'));
+    $apiKey = \getenv('TELNYX_API_KEY') ?: null;
+    $from = \getenv('TELNYX_FROM') ?: null;
+    $to = \getenv('TELNYX_TO') ?: null;
+
+    if (!$apiKey || !$from || !$to) {
+        $this->markTestSkipped('TELNYX_API_KEY, TELNYX_FROM, and TELNYX_TO are required for this integration test.');
+    }
+
+    $sender = new Telnyx($apiKey, $from);
 
     $message = new SMS(
-        to: [\getenv('TELNYX_TO')],
+        to: [$to],
         content: 'Test Content',
-        from: \getenv('TELNYX_FROM')
+        from: $from
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$sender = new Telnyx(\getenv('TELNYX_API_KEY'));
// $message = new SMS(
// to: ['+18034041123'],
// content: 'Test Content',
// from: '+15005550006'
// );
$message = new SMS(
to: [\getenv('TELNYX_TO')],
content: 'Test Content',
from: \getenv('TELNYX_FROM')
);
$apiKey = \getenv('TELNYX_API_KEY') ?: null;
$from = \getenv('TELNYX_FROM') ?: null;
$to = \getenv('TELNYX_TO') ?: null;
if (!$apiKey || !$from || !$to) {
$this->markTestSkipped('TELNYX_API_KEY, TELNYX_FROM, and TELNYX_TO are required for this integration test.');
}
$sender = new Telnyx($apiKey, $from);
$message = new SMS(
to: [$to],
content: 'Test Content',
from: $from
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Messaging/Adapter/SMS/TelnyxTest.php` around lines 16 - 22, The test
fails because Telnyx is instantiated with too few arguments and doesn't guard
missing env vars; update the test in TelnyxTest to first read and validate
getenv('TELNYX_API_KEY'), getenv('TELNYX_TO') and getenv('TELNYX_FROM') and call
markTestSkipped (or return) if any are empty, then construct the Telnyx sender
passing the required from argument (e.g., include the from value when calling
new Telnyx(...)) and create the SMS using the validated env values before
calling send.

@deepshekhardas deepshekhardas force-pushed the feat/7811-telnyx-adapter branch from e416217 to 4621f3b Compare June 2, 2026 01:55
@deepshekhardas
Copy link
Copy Markdown
Author

Rebased onto latest main. Could you please review? 🙏

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR refactors the Telnyx SMS adapter to send one API request per recipient (enabling per-recipient delivery tracking and error reporting) and makes $from a required constructor argument. The test suite is also uncommented and wired to environment variables.

  • Telnyx.php: $from is now a required constructor argument (dropping the message->getFrom() fallback), and process() loops per recipient with granular error messages from the Telnyx API response.
  • TelnyxTest.php: The previously skipped test is activated using TELNYX_API_KEY, TELNYX_TO, and TELNYX_FROM env vars — but the constructor call is missing the required $from argument, causing a fatal error before the test can execute.

Confidence Score: 2/5

The test file will crash immediately with a fatal PHP error due to a missing required constructor argument, meaning the new test coverage provides no validation until fixed.

The constructor signature change in Telnyx.php is correct, but the test was not updated to match — it still calls new Telnyx with one argument while the constructor now requires two. This blocking mistake prevents the test from running at all.

tests/Messaging/Adapter/SMS/TelnyxTest.php needs the $from argument added to the Telnyx constructor call before this can be merged.

Important Files Changed

Filename Overview
src/Utopia/Messaging/Adapter/SMS/Telnyx.php Refactors process() to loop per-recipient with accurate error reporting; $from is now a required constructor argument and the message->getFrom() fallback is removed. The foreach loop is redundant since getMaxMessagesPerRequest()=1 guarantees a single recipient.
tests/Messaging/Adapter/SMS/TelnyxTest.php Uncomments the test but passes only one argument to the Telnyx constructor, which now requires two; this will cause a fatal PHP error and the test will not run.

Reviews (1): Last reviewed commit: "feat: add Telnyx SMS adapter (#7811)" | Re-trigger Greptile

Comment on lines +16 to +22
$sender = new Telnyx(\getenv('TELNYX_API_KEY'));

// $message = new SMS(
// to: ['+18034041123'],
// content: 'Test Content',
// from: '+15005550006'
// );
$message = new SMS(
to: [\getenv('TELNYX_TO')],
content: 'Test Content',
from: \getenv('TELNYX_FROM')
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 The Telnyx constructor now requires two arguments ($apiKey and $from), but only one is passed here. This will throw a PHP fatal error — "Too few arguments to function Telnyx::__construct(), 1 passed and exactly 2 expected" — making the entire test suite fail immediately. The TELNYX_FROM env var is already used for the SMS message, but needs to be passed to the adapter constructor as well.

Suggested change
$sender = new Telnyx(\getenv('TELNYX_API_KEY'));
// $message = new SMS(
// to: ['+18034041123'],
// content: 'Test Content',
// from: '+15005550006'
// );
$message = new SMS(
to: [\getenv('TELNYX_TO')],
content: 'Test Content',
from: \getenv('TELNYX_FROM')
);
$sender = new Telnyx(\getenv('TELNYX_API_KEY'), \getenv('TELNYX_FROM'));
$message = new SMS(
to: [\getenv('TELNYX_TO')],
content: 'Test Content',
);

Comment on lines +46 to 67
foreach ($message->getTo() as $to) {
$result = $this->request(
method: 'POST',
url: 'https://api.telnyx.com/v2/messages',
headers: [
'Content-Type: application/json',
"Authorization: Bearer {$this->apiKey}",
],
body: [
'from' => $this->from,
'to' => $to,
'text' => $message->getContent(),
]
);

if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->setDeliveredTo(\count($message->getTo()));
foreach ($message->getTo() as $to) {
if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->incrementDeliveredTo();
$response->addResult($to);
}
} else {
foreach ($message->getTo() as $to) {
$response->addResult($to, 'Unknown error.');
} else {
$response->addResult($to, $result['response']['errors'][0]['detail'] ?? 'Unknown error');
}
}
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 Since getMaxMessagesPerRequest() returns 1, the parent send() method enforces that process() is always called with exactly one recipient and throws an exception otherwise. The foreach loop will therefore always iterate exactly once, making it misleading and inconsistent with the Twilio adapter (which uses $message->getTo()[0] directly). Accessing the single recipient directly is clearer and aligns with how other single-recipient adapters in this codebase are implemented.

Suggested change
foreach ($message->getTo() as $to) {
$result = $this->request(
method: 'POST',
url: 'https://api.telnyx.com/v2/messages',
headers: [
'Content-Type: application/json',
"Authorization: Bearer {$this->apiKey}",
],
body: [
'from' => $this->from,
'to' => $to,
'text' => $message->getContent(),
]
);
if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->setDeliveredTo(\count($message->getTo()));
foreach ($message->getTo() as $to) {
if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->incrementDeliveredTo();
$response->addResult($to);
}
} else {
foreach ($message->getTo() as $to) {
$response->addResult($to, 'Unknown error.');
} else {
$response->addResult($to, $result['response']['errors'][0]['detail'] ?? 'Unknown error');
}
}
$to = $message->getTo()[0];
$result = $this->request(
method: 'POST',
url: 'https://api.telnyx.com/v2/messages',
headers: [
'Content-Type: application/json',
"Authorization: Bearer {$this->apiKey}",
],
body: [
'from' => $this->from,
'to' => $to,
'text' => $message->getContent(),
]
);
if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->setDeliveredTo(1);
$response->addResult($to);
} else {
$response->addResult($to, $result['response']['errors'][0]['detail'] ?? 'Unknown error');
}

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!

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