-
Notifications
You must be signed in to change notification settings - Fork 259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: mail provider backend #9651
Conversation
Todo:
|
This PR requires the server PR first... |
TODO: Separate attachment handling for file system files (saved on the system) and string data files (created or loaded in memory) |
TODO: unit tests |
Hello there, We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process. Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6 Thank you for contributing to Nextcloud and we hope to hear from you soon! |
ac83ff7
to
db9dc0e
Compare
b3a5ab4
to
35075d7
Compare
3a1d94a
to
ece67ba
Compare
Testing process: Pull this PR and server PR - nextcloud/server#45383 Make sure you have an email account setup in the mail app. Then create calendar event with an attendee. Invitation email should be sent from personal mail account |
221745f
to
20dc43a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove all @since
comments. Also, comments that only describe what the variable name / return type already documents can be removed, too.
* | ||
* @throws ClientException | ||
*/ | ||
public function findByUserIdAndAddress(string $userId, string $address): array { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public function findByUserIdAndAddress(string $userId, string $address): array { | |
public function findByUserIdAndAddress(string $userId, string $email): array { |
ade51c2
to
a9898e6
Compare
a9898e6
to
d15711e
Compare
lib/Provider/Command/MessageSend.php
Outdated
* | ||
* @param array<int,IAddress> $addresses collection of IAddress objects | ||
* | ||
* @return array<int,array{email: string, label?: string}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @return array<int,array{email: string, label?: string}> | |
* @return array<int,array{email: ?string, label?: ?string}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This didn't work, caused more Psalm errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERROR: InvalidArgument - lib/Provider/Command/MessageSend.php:109:5 - Argument 3 of OCA\Mail\Service\OutboxService::saveMessage expects array<int, array<array-key, string>>, but array<int, array{email: null|string, label?: string}> provided (see https://psalm.dev/004)
$to,
ERROR: InvalidArgument - lib/Provider/Command/MessageSend.php:110:5 - Argument 4 of OCA\Mail\Service\OutboxService::saveMessage expects array<int, array<array-key, string>>, but array<int, array{email: null|string, label?: string}> provided (see https://psalm.dev/004)
$cc,
ERROR: InvalidArgument - lib/Provider/Command/MessageSend.php:111:5 - Argument 5 of OCA\Mail\Service\OutboxService::saveMessage expects array<int, array<array-key, string>>, but array<int, array{email: null|string, label?: string}> provided (see https://psalm.dev/004)
$bcc,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/**
* Converts IAddress objects collection to plain array
*
* @since 4.0.0
*
* @param array<int,IAddress> $addresses collection of IAddress objects
*
* @return array<int,array{email: ?string, label?: string}>
*/
protected function convertAddressArray(array $addresses): array {
return array_map(static function (IAddress $address) {
$label = $address->getLabel();
return !empty($label)
? ['email' => $address->getAddress(), 'label' => $label]
: ['email' => $address->getAddress()];
}, $addresses);
}
Please update the function as above.
- The annotated return type for label is always a string.
- It's required to introduce a local variable for label, otherwise psalm is unable to detect.
lib/Provider/Command/MessageSend.php
Outdated
} | ||
} | ||
// determine if required To address is set | ||
if (empty($message->getTo()) || empty($message->getTo()[0]->getAddress())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easiest way to fix the psalm warning about possibly null: Introduce a local variable for to.
A bit nicer: Move the validation to a function.
/**
* @param IAddress[] $addresses
* @throws SendException
*/
private function validateTo(array $addresses): void
{
if (count($addresses) === 0 || empty($addresses[0]->getAddress())) {
throw new SendException('Invalid Message Parameter: MUST contain at least one TO address with a valid address');
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seem to be the important changes remaining
lib/Provider/MailProvider.php
Outdated
public function listServices(string $userId): array { | ||
// retrieve service(s) details from data store | ||
try { | ||
$accounts = $this->accountService->findByUserId($userId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
findByUserId
does not throw anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, findByUserId it self does not throw an error. But wouldn't it be better to catch any and all errors and log them? That way the mail manager does not have to handle any/all the different errors that might occur with the different providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no reason for a catch-all right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
lib/Provider/Command/MessageSend.php
Outdated
$entry->getType(), | ||
$entry->getContents() | ||
)->jsonSerialize(); | ||
} catch (\Throwable $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} catch (\Throwable $e) { | |
} catch (UploadException $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to catch any and all errors and present them as one type? That way the consuming app/service does not have to handle any/all the different errors that might occur. For instance, the addFileFromString function currently does not thrown a proper UploadException for any database errors, so if the filename is too long or the mime type is too long, this would cause a different error.
$this->mailAttachment->getType(), | ||
$this->mailAttachment->getContents() | ||
)->will( | ||
$this->throwException(new \Exception('Something went wrong')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$this->throwException(new \Exception('Something went wrong')) | |
$this->throwException(new UploadException('Something went wrong')) |
lib/Service/AccountService.php
Outdated
return array_map(static function ($a) { | ||
return new Account($a); | ||
}, $this->mapper->findByUserIdAndAddress($userId, $address)); | ||
} catch (DoesNotExistException $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SebastianKrupinski just remove the try-catch. There is no exception to expect from this method. The db exception is not an expected runtime exception either, hence not documented in the mapper method.
The branch is a bit behind main. |
167374a
to
7f85f0d
Compare
@SebastianKrupinski to provide the reason for this: it allows us to follow your changes. Every time you change the git history with an amend or a rebase, github is no longer able to diff the changes between our last visit and your last push. Every time you change history, we have to review the full diff. In this PR, that's 1200 LOC. Hope this makes sense. Please avoid rebases in PRs like this where reviewers left request for changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The remaining must haves
lib/Provider/Command/MessageSend.php
Outdated
$bcc, | ||
$attachments | ||
); | ||
} catch (\Throwable $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could not find any documented exception to be thrown by saveMessage
. Remove the try-catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left this in one in on purpose, the saveMessage function actually spawns a Imap connection, so I though it would be a good idea to remove the local attachments if the function fails to prevent ghost attachment entries in the database.
Removed
private $attachmentService; | ||
/** @var MessageSend */ | ||
private $commandSend; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is spamming the logs on CI and when you run tests locally. I strongly suggest to fix this in this PR. The required effort is low.
Sorry, i did a merge the first time and it messed up the app, couldn't get it to run, so after trying to fix it, I had to delete it re-clone it and redo the changes. |
*/ | ||
protected function purgeSavedAttachments(array $attachments): void { | ||
foreach ($attachments as $attachment) { | ||
$this->attachmentService->deleteAttachment($attachment->getUserId(), $attachment->getId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getUserId and getId only work when you pass an LocalAttachment object. You are calling jsonSerialize in Line 95, and therefore we have a plain array here.
I prefer working with real objects rather than plain arrays and therefore recommend removing jsonSerialize in Line 95, add the phpdoc back for purgeSavedAttachmends, and use array_map(static fn(LocalAttachment $attachment) => $attachment->jsonSerialize(), $attachments)
in Line 109.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I fixed the oversight with the LocalAttachment. But i left he phpdoc as paslm is still not happy and I don't want miss another deadline because of paslm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@param array<int, LocalAttachment> $attachments collection of local attachment objects
These two comments... #9651 (comment) From the selected code block, I think you are looking at a old version of the PR the code was already changed in this commit. 7f85f0d#diff-b2fd4b9c63f5641101d4ec474c50008f95fc16af2c64ae2972a22e607f88932d |
See https://github.com/nextcloud/mail/actions/runs/10755754956/job/29827858067?pr=9651. This is the latest CI run. |
Okay, I found the confusion it was a different variable then what was highlighted. Fixed. |
@SebastianKrupinski please rebase & squash into one commit |
Signed-off-by: SebastianKrupinski <[email protected]>
fd7ceff
to
ce653d0
Compare
@ChristophWurst done |
Initial Mail Provider Implementation
nextcloud/server#5080