feat(HDNEXT-1010): intercept file-request send-email via IONOS mailer API - #17
Conversation
35d3a58 to
f555ea2
Compare
There was a problem hiding this comment.
Pull request overview
Adds support for routing “send-email” (share email resend) requests through the IONOS mailer API by introducing a new listener for BeforeShareMailNotifiedEvent, while also hardening existing share-created handling against empty recipients to prevent IONOS API errors.
Changes:
- Add
BeforeShareMailNotifiedEventListenerto intercept email resend events and forward them toIonosMailerService. - Add an early-return guard in
ShareCreatedEventListenerwhengetSharedWith()is empty. - Extend unit tests and test bootstrap to support the new event (via stub fallback) and validate listener registrations.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
lib/Listener/ShareCreatedEventListener.php |
Adds empty-recipient guard before sending to the mailer service. |
lib/Listener/BeforeShareMailNotifiedEventListener.php |
New listener for resend/send-email flow, sends share-by-link payload via IONOS mailer. |
lib/AppInfo/Application.php |
Registers the new listener in the app bootstrap. |
tests/Listener/ShareCreatedEventListenerTest.php |
Adds unit test for empty-recipient guard behavior. |
tests/Listener/BeforeShareMailNotifiedEventListenerTest.php |
New unit tests for the new listener’s behavior and error paths. |
tests/stubs/BeforeShareMailNotifiedEvent.php |
Provides test-only fallback event class when not available in the runtime. |
tests/bootstrap.php |
Loads the fallback event stub conditionally for unit tests. |
tests/AppInfo/ApplicationTest.php |
Updates expectations to verify both event listener registrations. |
psalm.xml |
Adds the new event stub file to Psalm stubs to satisfy static analysis. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
24adfb6 to
892cc4c
Compare
…lock Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…n composer.lock Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…it to version ^8.5.52 || ^9.6.34 in composer.lock Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
da3dcb8 to
fbce3b2
Compare
…lSentEventListener (HDNEXT-1010) Delivery is now triggered by BeforeShareMailSentEvent fired from inside ShareByMailProvider.sendEmail() (nc-server PR#262). This covers both Manager::createShare() and the sendShareEmail() Controller path with a single listener — eliminating the previous double-send on regular shares. The event carries pre-computed mail data (senderUserId, fileName, resourceUrl, note, expiration) so the listener no longer needs IUserManager or IURLGenerator injections. markMailHandled() is called unconditionally in every TYPE_EMAIL code path so native Nextcloud SMTP is suppressed in all branches. IONOS send failures propagate as exceptions without falling back to SMTP. Non-TYPE_EMAIL share types are not marked handled — native SMTP still runs for them. Retires ShareCreatedEventListener which caused double-sends when combined with the native sendMailNotification() call in Manager::createShare(). Adds tests/stubs/BeforeShareMailSentEvent.php for CI environments without the nc-server sharebymail app. Adds sharebymail event path to psalm.xml for static analysis.
…NEXT-1010) BeforeShareMailSentEvent now exposes named typed getters (getSenderUserId(), getFileName(), getResourceUrl(), getNote(), getExpiration()) instead of a generic getMailData(): array<string,mixed>. Drop all defensive is_string() / null checks in the listener — types are guaranteed by the event class. Remove testMissingSenderUserIdMarkHandledAndLogsError (the corresponding listener guard no longer exists). Update the CI stub and makeEvent() to use the new constructor shape ($templateData with Nextcloud template-convention key names: 'filename', 'link', 'initiator', … instead of the old camelCase event keys).
The class_exists() guard fired during nc_ionos_processes::register(), which runs before sharebymail's Composer autoloader (apps/sharebymail/composer/autoload.php) is loaded. This caused class_exists() to return false, leaving the listener unregistered for the entire request. registerEventListener() stores only class name strings — no instantiation happens at registration time — so guarding on class_exists is both unnecessary and harmful here. If sharebymail is not installed, the event is never dispatched and the listener is never called regardless. Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
- composer.json: use explicit vendor-bin path for php-cs-fixer instead of relying on PATH. CI uses system php-cs-fixer 3.82.2 which predates the modifier_keywords rule required by nextcloud/coding-standard v1.5.0 (^3.87) - psalm.xml: replace monorepo-relative ../../apps/sharebymail/lib/Event path with tests/stubs which contains equivalent type stubs and works in CI where nc_ionos_processes is checked out standalone Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…nd no-SMTP-fallback on exception - testUnrelatedEventIsIgnored: plain Event passed to handle() — instanceof guard returns early, mailer never called - testMultipleRecipientsAreAllPassed: all recipients forwarded to IONOS API - testMailHandledBeforeSendSoNoSmtpFallbackOnMailerException: markMailHandled() is called before send(), so isMailHandled() stays true even when the IONOS API throws — native SMTP fallback is suppressed regardless of delivery failure Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
Code reviewNo issues found. Checked for bugs, event registration correctness, listener logic (TYPE_EMAIL guard, markMailHandled ordering, empty-recipients path), test coverage, and consistency with nc-server PR #262. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 16 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
lib/Service/IonosMailerService.php:80
IonosMailerService::send()catches and swallows exceptions fromprocessShareByLinkEvent(). With the new listener callingmarkMailHandled()to suppress native SMTP, this results in a silent mail loss: the share mail is neither delivered via IONOS nor via SMTP, and no exception propagates to the caller (also contradicts the listener docstring/PR description about propagating failures). Consider rethrowing (or returning a success flag) so the caller can fail the request and/or trigger retries; update unit tests accordingly.
if ($eventName === BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK) {
$message = new ShareMessageByLink($variables);
try {
$this->logger->debug('Send message to mailer service', ['event' => $eventName, 'variables' => $variables]);
$apiInstance->processShareByLinkEvent(self::BRAND, $message);
} catch (Exception $e) {
$this->logger->error('Exception when calling EventAPIApi->processShareByLinkEvent', ['exception' => $e]);
}
Summary
Replaces
ShareCreatedEventListenerwithBeforeShareMailSentEventListener— the single handler for all share-by-link email delivery via the IONOS internal mail API.What changed
BeforeShareMailSentEventListenerhandlesBeforeShareMailSentEventfired byShareByMailProvider.sendEmail()(nc-server PR#262). CallsmarkMailHandled()unconditionally in every TYPE_EMAIL code path, suppressing native Nextcloud SMTP.ShareCreatedEventListenercaused double-sends — it called the IONOS API atShareCreatedEventtime while native SMTP also ran viasendMailNotification().IUserManagerandIURLGenerator— the event now carries pre-computedmailData(senderUserId, fileName, resourceUrl, note, expiration).../../apps/sharebymail/lib/Eventas extra files so psalm resolves the event class.BeforeShareMailSentEventstub for CI environments without nc-server.markMailHandled() coverage
Related PRs
Test plan
composer test:unit— 14/14 passcomposer cs:fix— no changescomposer psalm— no errors