Skip to content

Remove to from MessagePrecacheDto#381

Merged
TatevikGr merged 5 commits intodevfrom
ref/email-to-in-dto
Feb 16, 2026
Merged

Remove to from MessagePrecacheDto#381
TatevikGr merged 5 commits intodevfrom
ref/email-to-in-dto

Conversation

@TatevikGr
Copy link
Contributor

@TatevikGr TatevikGr commented Feb 16, 2026

Summary by CodeRabbit

  • Refactor
    • Recipient addresses are now passed explicitly throughout the email pipeline for clearer, more reliable message construction and handling.
  • Refactor
    • Subscriber creation now requires an email at instantiation, enforcing consistent initialization.
  • Bug Fixes
    • Admin notification filtering relaxed so system notifications are handled more consistently even when recipient info is missing.

Thanks for contributing to phpList!

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Warning

Rate limit exceeded

@TatevikGr has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 47 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.

📝 Walkthrough

Walkthrough

Message recipient handling was refactored: MessagePrecacheDto's to property removed and builders/content constructors now accept an explicit toEmail and a resolved Subscriber receiver; handlers and tests updated to pass/use toEmail and construct Subscribers with a required email constructor.

Changes

Cohort / File(s) Summary
DTO & population
src/Domain/Messaging/Model/Dto/MessagePrecacheDto.php, src/Domain/Messaging/Service/MessageDataLoader.php, src/Domain/Messaging/Service/MessagePrecacheService.php
Removed public ?string $to and stopped populating/returning the tofield into the DTO.
Core builders (signatures & usage)
src/Domain/Messaging/Service/Builder/BaseEmailBuilder.php, src/Domain/Messaging/Service/Builder/EmailBuilder.php, src/Domain/Messaging/Service/Builder/SystemEmailBuilder.php
Added explicit toEmail parameter to campaign/system build methods; replaced data->to with toEmail for validation, blacklist checks, header population and base email creation; EmailBuilder resolves Subscriber by email and may throw SubscriberNotFoundException.
Content constructors / forward builder
src/Domain/Messaging/Service/Constructor/CampaignMailContentBuilder.php, src/Domain/Messaging/Service/Builder/ForwardEmailBuilder.php
CampaignMailContentBuilder now accepts a resolved Subscriber $receiver (removed internal lookup); ForwardEmailBuilder initializes/uses a resolved receiver and passes it to content builder.
Handlers / senders
src/Domain/Identity/Service/AdminCopyEmailSender.php, src/Domain/Messaging/MessageHandler/CampaignProcessorMessageHandler.php
Removed assignments to data->to; updated calls to builders to pass recipient via toEmail (admin/report/subscriber notifications).
Subscriber model & creation sites
src/Domain/Subscription/Model/Subscriber.php, src/Domain/Subscription/Service/Manager/SubscriberManager.php, tests/..., tests/Integration/...
Subscriber constructor now requires string $email; manager, fixtures and many tests updated to construct new Subscriber($email) instead of setting email after construction.
Tests
tests/Unit/..., tests/Integration/... (many files)
Widespread test updates: removed DTO to usage, passed toEmail into builder calls, updated CampaignMailContentBuilder tests to pass a Subscriber receiver, and adjusted many tests to the changed Subscriber constructor.
Minor formatting
src/Domain/Messaging/Service/Builder/BaseEmailBuilder.php
Trivial formatting edits only.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Handler as CampaignProcessorMessageHandler
participant Builder as EmailBuilder / SystemEmailBuilder
participant Repo as SubscriberRepository
participant Content as CampaignMailContentBuilder
participant Mailer as MailTransport/Mailer

Handler->>Builder: buildCampaignEmail(messageId, data, toEmail)
Builder->>Repo: findOneByEmail(toEmail)
Repo-->>Builder: Subscriber (or null)
alt subscriber found
    Builder->>Content: __invoke(data, receiver, campaignId)
    Content-->>Builder: assembled content
    Builder->>Mailer: send email (toEmail, headers, content)
    Mailer-->>Handler: delivery result
else subscriber not found
    Builder-->>Handler: throw SubscriberNotFoundException / return null
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change across the entire changeset: removing the 'to' property from MessagePrecacheDto and refactoring email recipient handling throughout the codebase.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into dev

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ref/email-to-in-dto

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

@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

🤖 Fix all issues with AI agents
In `@src/Domain/Messaging/MessageHandler/CampaignProcessorMessageHandler.php`:
- Around line 243-247: The call to
$this->configProvider->getValue(ConfigOption::ReportAddress) returns ?string but
is passed into SystemEmailBuilder::buildCampaignEmail which expects string,
risking a TypeError; update CampaignProcessorMessageHandler:: (the block
creating $email) to guard or normalize the value—either coalesce to a safe
default/email (e.g. null-coalesce) or validate and throw/log if missing before
calling SystemEmailBuilder::buildCampaignEmail, ensuring the $toEmail argument
is always a non-null string.

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/Domain/Subscription/Model/Subscriber.php (1)

112-120: ⚠️ Potential issue | 🔴 Critical

Fix the mock in SubscriberBlacklistManagerTest.php — it needs disableOriginalConstructor().

The constructor now requires string $email, which breaks one test file: tests/Unit/Domain/Subscription/Service/Manager/SubscriberBlacklistManagerTest.php at line 132. The mock builder there is missing ->disableOriginalConstructor(), so it tries to invoke the real constructor without the required parameter.

All other test mocks already have it in place. Just add the call to that one chain.

tests/Unit/Domain/Messaging/Service/ForwardContentServiceTest.php (1)

55-66: ⚠️ Potential issue | 🟡 Minor

Fix argument order in testThrowsWhenCacheMissing.

The 3rd and 4th arguments to MessageForwardDto are swapped in this test. The constructor expects (emails, uid, fromName, fromEmail, note), but the first call has them as (emails, uid, 'from@example.com', 'From', note). The second test method shows the correct order: (emails, uid, 'From Name', 'from@example.com', note). While the exception fires before these values are accessed, the test data should still match the intended parameter types for clarity.

tests/Unit/Domain/Subscription/Model/SubscriberTest.php (1)

71-74: ⚠️ Potential issue | 🔴 Critical

Test will fail — constructor now sets the email.

The pipeline confirms this: testGetEmailInitiallyReturnsEmptyString asserts '' but getEmail() returns 'test@example.com' because the constructor now accepts and stores the email. Either update the assertion to match the constructor argument, or rename/remove this test since "initially returns empty string" no longer applies.

Proposed fix
-    public function testGetEmailInitiallyReturnsEmptyString(): void
+    public function testGetEmailInitiallyReturnsConstructorValue(): void
     {
-        self::assertSame('', $this->subscriber->getEmail());
+        self::assertSame('test@example.com', $this->subscriber->getEmail());
     }
🤖 Fix all issues with AI agents
In `@src/Domain/Messaging/Service/Builder/EmailBuilder.php`:
- Around line 117-122: The test failing with AttachmentException is hitting the
new subscriber lookup in EmailBuilder (subscriberRepository->findOneByEmail) and
throwing SubscriberNotFoundException before attachment logic runs; update the
failing test to stub/mocking of subscriberRepository->findOneByEmail to return a
valid Subscriber instance (or otherwise seed a Subscriber) so attachment path
executes and AttachmentException can be asserted; locate usage of EmailBuilder
and its dependency injection in the test, mock the repository method to return a
Subscriber object instead of null, and rerun the attachment tests.
🧹 Nitpick comments (3)
src/Domain/Subscription/Service/Manager/SubscriberManager.php (1)

43-53: Minor inconsistency in persist strategy (pre-existing).

createSubscriber delegates to $this->subscriberRepository->persist() while createFromImport calls $this->entityManager->persist() directly. Not introduced by this PR, but worth aligning when convenient.

Also applies to: 106-121

tests/Integration/Domain/Subscription/Fixtures/SubscriberFixture.php (1)

31-38: Pre-existing: $headers from fgetcsv isn't validated before use in array_combine.

If the CSV is empty or malformed, fgetcsv returns false/null/[], and array_combine($headers, $data) on line 38 would blow up. Not introduced by this PR, but worth a quick guard if you're in the neighborhood.

Suggested guard
         $headers = fgetcsv($handle);
+        if ($headers === false || $headers === [null]) {
+            fclose($handle);
+            throw new RuntimeException(sprintf('Fixture file "%s" has no valid header row.', $csvFile));
+        }
src/Domain/Messaging/Service/Builder/EmailBuilder.php (1)

119-121: Consider not embedding the email address in the exception message.

Including the raw email in the exception string (sprintf('Subscriber with email %s not found', $toEmail)) risks leaking PII into logs or error-reporting systems. A more privacy-friendly approach would be to use a generic message and pass context separately if needed for debugging.

Suggested diff
-            throw new SubscriberNotFoundException(
-                sprintf('Subscriber with email %s not found', $toEmail)
-            );
+            throw new SubscriberNotFoundException('Subscriber not found for the given email');

Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Domain/Messaging/MessageHandler/CampaignProcessorMessageHandler.php (1)

271-284: ⚠️ Potential issue | 🟡 Minor

Notification addresses from explode may carry whitespace.

explode(',', ...) on "alice@example.com, bob@example.com" yields " bob@example.com" with a leading space. Passing that straight into toEmail could cause address-parsing issues downstream. A quick trim() keeps things tidy.

Suggested tweak
             $notifications = explode(',', $loadedMessageData['notify_start']);
             foreach ($notifications as $notification) {
+                $notification = trim($notification);
+                if ($notification === '') {
+                    continue;
+                }
                 $data = new MessagePrecacheDto();
tests/Unit/Domain/Messaging/Service/Builder/EmailBuilderTest.php (1)

171-175: ⚠️ Potential issue | 🟠 Major

Mock expectation mismatch — __invoke receives 3 arguments, but test expects only 1.

The implementation at EmailBuilder.php lines 124-128 invokes mailContentBuilder with three named arguments (messagePrecacheDto, receiver, campaignId), but the test mock at line 174 is set up with ->with($dto), which PHPUnit enforces as exactly one argument. This will fail at runtime.

Proposed fix
         $this->mailConstructor
             ->expects($this->once())
             ->method('__invoke')
-            ->with($dto)
+            ->with($dto, $this->isInstanceOf(Subscriber::class), 777)
             ->willReturn(['<p>HTML</p>', 'TEXT']);
🧹 Nitpick comments (1)
tests/Unit/Domain/Messaging/Service/Builder/EmailBuilderTest.php (1)

413-414: Nit: stray blank changed line.

Line 414 is marked as changed but is just a blank line — no functional issue, just noise in the diff.

@TatevikGr TatevikGr merged commit 3a1ab4e into dev Feb 16, 2026
7 checks passed
@TatevikGr TatevikGr deleted the ref/email-to-in-dto branch February 16, 2026 09:22
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