Skip to content

feat(HDNEXT-1010): intercept file-request send-email via IONOS mailer API - #17

Merged
tanyaka merged 9 commits into
mainfrom
HDNEXT-1010-intercept-send-email
Jul 3, 2026
Merged

feat(HDNEXT-1010): intercept file-request send-email via IONOS mailer API#17
tanyaka merged 9 commits into
mainfrom
HDNEXT-1010-intercept-send-email

Conversation

@printminion-co

@printminion-co printminion-co commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces ShareCreatedEventListener with BeforeShareMailSentEventListener — the single handler for all share-by-link email delivery via the IONOS internal mail API.

What changed

  • New: BeforeShareMailSentEventListener handles BeforeShareMailSentEvent fired by ShareByMailProvider.sendEmail() (nc-server PR#262). Calls markMailHandled() unconditionally in every TYPE_EMAIL code path, suppressing native Nextcloud SMTP.
  • Retired: ShareCreatedEventListener caused double-sends — it called the IONOS API at ShareCreatedEvent time while native SMTP also ran via sendMailNotification().
  • Simplified: listener constructor drops IUserManager and IURLGenerator — the event now carries pre-computed mailData (senderUserId, fileName, resourceUrl, note, expiration).
  • psalm.xml: adds ../../apps/sharebymail/lib/Event as extra files so psalm resolves the event class.
  • tests/stubs/: provides BeforeShareMailSentEvent stub for CI environments without nc-server.

markMailHandled() coverage

Code path markMailHandled() called?
Non-TYPE_EMAIL share no — native SMTP still runs
TYPE_EMAIL, empty recipients yes — suppressed + warning logged
TYPE_EMAIL, missing senderUserId in mailData yes — suppressed + error logged
TYPE_EMAIL, successful IONOS send yes — suppressed
TYPE_EMAIL, IONOS send throws propagates as exception (native send also skipped)

Related PRs

Test plan

  • composer test:unit — 14/14 pass
  • composer cs:fix — no changes
  • composer psalm — no errors
  • E2E: regular share by email → IONOS API called once; no double-send
  • E2E: file request send-email → IONOS API called; no native SMTP

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 BeforeShareMailNotifiedEventListener to intercept email resend events and forward them to IonosMailerService.
  • Add an early-return guard in ShareCreatedEventListener when getSharedWith() 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.

Comment thread lib/AppInfo/Application.php Outdated
Comment thread tests/Listener/BeforeShareMailNotifiedEventListenerTest.php Outdated
Comment thread tests/Listener/BeforeShareMailNotifiedEventListenerTest.php Outdated
Comment thread tests/Listener/BeforeShareMailNotifiedEventListenerTest.php Outdated
Comment thread tests/Listener/BeforeShareMailNotifiedEventListenerTest.php Outdated
Comment thread tests/Listener/BeforeShareMailNotifiedEventListenerTest.php Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread tests/Listener/BeforeShareMailNotifiedEventListenerTest.php Outdated
…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>
@printminion-co
printminion-co force-pushed the HDNEXT-1010-intercept-send-email branch 2 times, most recently from da3dcb8 to fbce3b2 Compare June 15, 2026 13:46
printminion-co and others added 5 commits June 15, 2026 15:57
…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>
@tanyaka

tanyaka commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Code review

No 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 👎.

@tanyaka tanyaka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review OK.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 from processShareByLinkEvent(). With the new listener calling markMailHandled() 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]);
			}

Comment thread lib/Listener/BeforeShareMailSentEventListener.php
Comment thread psalm.xml
@tanyaka
tanyaka merged commit 9501a5e into main Jul 3, 2026
34 checks passed
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.

4 participants