Skip to content

Add SparkPost email adapter - #146

Closed
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:fix/pr-47-sparkpost
Closed

Add SparkPost email adapter#146
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:fix/pr-47-sparkpost

Conversation

@deepshekhardas

Copy link
Copy Markdown

Rebased on latest main with code adaptation.

Adapted from original PR #47 (by ShivamChandra09):

  • Moved file from Adapters/Email/ to Adapter/Email/
  • Updated namespace from Utopia\Messaging\Adapters\Email to Utopia\Messaging\Adapter\Email
  • Added parent::__construct() call
  • Updated process() return type from string to array
  • Fixed request() call to pass array body (not json_encoded)
  • Added proper response handling using Response class
  • Dropped CI/docker changes (outdated)

@github-actions

Copy link
Copy Markdown

Thanks for contributing! This repository is a read-only mirror; development for this library happens in packages/messaging in the utopia-php monorepo. Please open this pull request there instead.

@github-actions github-actions Bot closed this Jul 25, 2026
@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a SparkPost email adapter and an end-to-end delivery test.

  • Selects the US or EU SparkPost transmissions endpoint.
  • Builds text or HTML transmission payloads and maps HTTP responses into the messaging Response format.
  • Adds an environment-configured SparkPost end-to-end test.

Confidence Score: 1/5

This PR is not safe to merge because the adapter cannot successfully process a valid Email and its end-to-end test cannot construct the test message.

The adapter calls a nonexistent sender method, mishandles normalized recipient records, drops additional To, CC, and BCC recipients, fails on non-JSON error bodies, and leaves successful delivery counts at zero; the accompanying test also omits mandatory constructor arguments.

Files Needing Attention: src/Utopia/Messaging/Adapter/Email/SparkPost.php and tests/e2e/Email/SparkPostTest.php

Important Files Changed

Filename Overview
src/Utopia/Messaging/Adapter/Email/SparkPost.php Adds the adapter, but sender and recipient handling prevent valid sends, additional recipient groups are omitted, error bodies are handled unsafely, and successful responses report an incorrect delivery count.
tests/e2e/Email/SparkPostTest.php Adds a basic delivery test, but its Email constructor call omits required sender arguments and cannot execute.

Fix All in Claude Code Fix All in Codex

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

---

### Issue 1 of 7
src/Utopia/Messaging/Adapter/Email/SparkPost.php:56
**Undefined sender accessor crashes sends**

When any valid email is sent, this calls `Email::getFrom()`, but the message exposes only `getFromEmail()` and `getFromName()`, causing an undefined-method error before the SparkPost request is made.

### Issue 2 of 7
src/Utopia/Messaging/Adapter/Email/SparkPost.php:50
**Recipient records used as strings**

`getTo()[0]` is a normalized recipient array, not an address string. This places an object in SparkPost's `address.email` field, and the same value passed to `Response::addResult()` causes a type error because that method requires a string.

### Issue 3 of 7
src/Utopia/Messaging/Adapter/Email/SparkPost.php:50
**Additional recipients are silently dropped**

When an email has 2–1000 `to` recipients, the base adapter accepts it and invokes `process()` once, but this payload includes only `getTo()[0]`. Every remaining recipient is omitted from both delivery and response results.

### Issue 4 of 7
src/Utopia/Messaging/Adapter/Email/SparkPost.php:65-66
**Successful delivery count remains zero**

When SparkPost accepts a transmission, this branch adds a successful result but never calls `setDeliveredTo()`. The returned response therefore reports zero deliveries even though the email was sent, breaking delivery accounting and response assertions.

### Issue 5 of 7
tests/e2e/Email/SparkPostTest.php:15-19
**Required sender arguments are omitted**

Running this test constructs `Email` without its required `fromName` and `fromEmail` arguments, causing an argument-count error before the adapter is invoked.

### Issue 6 of 7
src/Utopia/Messaging/Adapter/Email/SparkPost.php:47-52
**CC and BCC recipients are omitted**

When an email contains CC or BCC recipients, the transmission body includes only the first `to` recipient and never adds either recipient group, causing those recipients to be silently excluded from delivery.

### Issue 7 of 7
src/Utopia/Messaging/Adapter/Email/SparkPost.php:68
**Non-JSON errors break response handling**

When SparkPost or an intermediary returns a plain-text or HTML error body, the request helper preserves `response` as a string, but this line accesses it as a nested array. Error handling then raises an offset error instead of returning the provider's failure response.

Reviews (1): Last reviewed commit: "feat: add SparkPost email adapter" | Re-trigger Greptile

],
'content' => [
'from' => [
'email' => $message->getFrom(),

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 Undefined sender accessor crashes sends

When any valid email is sent, this calls Email::getFrom(), but the message exposes only getFromEmail() and getFromName(), causing an undefined-method error before the SparkPost request is made.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Email/SparkPost.php
Line: 56

Comment:
**Undefined sender accessor crashes sends**

When any valid email is sent, this calls `Email::getFrom()`, but the message exposes only `getFromEmail()` and `getFromName()`, causing an undefined-method error before the SparkPost request is made.

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

Fix in Claude Code Fix in Codex

'recipients' => [
[
'address' => [
'email' => $message->getTo()[0],

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 Recipient records used as strings

getTo()[0] is a normalized recipient array, not an address string. This places an object in SparkPost's address.email field, and the same value passed to Response::addResult() causes a type error because that method requires a string.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Email/SparkPost.php
Line: 50

Comment:
**Recipient records used as strings**

`getTo()[0]` is a normalized recipient array, not an address string. This places an object in SparkPost's `address.email` field, and the same value passed to `Response::addResult()` causes a type error because that method requires a string.

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

Fix in Claude Code Fix in Codex

'recipients' => [
[
'address' => [
'email' => $message->getTo()[0],

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 Additional recipients are silently dropped

When an email has 2–1000 to recipients, the base adapter accepts it and invokes process() once, but this payload includes only getTo()[0]. Every remaining recipient is omitted from both delivery and response results.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Email/SparkPost.php
Line: 50

Comment:
**Additional recipients are silently dropped**

When an email has 2–1000 `to` recipients, the base adapter accepts it and invokes `process()` once, but this payload includes only `getTo()[0]`. Every remaining recipient is omitted from both delivery and response results.

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

Fix in Claude Code Fix in Codex

Comment on lines +65 to +66
if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->addResult($message->getTo()[0]);

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 Successful delivery count remains zero

When SparkPost accepts a transmission, this branch adds a successful result but never calls setDeliveredTo(). The returned response therefore reports zero deliveries even though the email was sent, breaking delivery accounting and response assertions.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Email/SparkPost.php
Line: 65-66

Comment:
**Successful delivery count remains zero**

When SparkPost accepts a transmission, this branch adds a successful result but never calls `setDeliveredTo()`. The returned response therefore reports zero deliveries even though the email was sent, breaking delivery accounting and response assertions.

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

Fix in Claude Code Fix in Codex

Comment on lines +15 to +19
$message = new Email(
to: [\getenv('TEST_EMAIL')],
subject: 'Test Subject',
content: 'Test Content',
);

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 Required sender arguments are omitted

Running this test constructs Email without its required fromName and fromEmail arguments, causing an argument-count error before the adapter is invoked.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/e2e/Email/SparkPostTest.php
Line: 15-19

Comment:
**Required sender arguments are omitted**

Running this test constructs `Email` without its required `fromName` and `fromEmail` arguments, causing an argument-count error before the adapter is invoked.

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

Fix in Claude Code Fix in Codex

Comment on lines +47 to +52
'recipients' => [
[
'address' => [
'email' => $message->getTo()[0],
],
],

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 CC and BCC recipients are omitted

When an email contains CC or BCC recipients, the transmission body includes only the first to recipient and never adds either recipient group, causing those recipients to be silently excluded from delivery.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Email/SparkPost.php
Line: 47-52

Comment:
**CC and BCC recipients are omitted**

When an email contains CC or BCC recipients, the transmission body includes only the first `to` recipient and never adds either recipient group, causing those recipients to be silently excluded from delivery.

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

Fix in Claude Code Fix in Codex

if ($result['statusCode'] >= 200 && $result['statusCode'] < 300) {
$response->addResult($message->getTo()[0]);
} else {
$error = $result['response']['errors'][0]['message'] ?? $result['error'] ?? '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.

P1 Non-JSON errors break response handling

When SparkPost or an intermediary returns a plain-text or HTML error body, the request helper preserves response as a string, but this line accesses it as a nested array. Error handling then raises an offset error instead of returning the provider's failure response.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Utopia/Messaging/Adapter/Email/SparkPost.php
Line: 68

Comment:
**Non-JSON errors break response handling**

When SparkPost or an intermediary returns a plain-text or HTML error body, the request helper preserves `response` as a string, but this line accesses it as a nested array. Error handling then raises an offset error instead of returning the provider's failure response.

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.

2 participants