Skip to content

Commit a003bbe

Browse files
authored
Merge pull request #11943 from dhairyajangir/fix/attachment-createdat-timestamp-and-migration
Enforce NOT NULL and Remove Default Value for mail_attachments.created_at Column
2 parents f1b7ac4 + 91ade16 commit a003bbe

File tree

6 files changed

+71
-6
lines changed

6 files changed

+71
-6
lines changed

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove
3434
3535
Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
3636
]]></description>
37-
<version>5.6.0-alpha.3</version>
37+
<version>5.6.0-alpha.4</version>
3838
<licence>agpl</licence>
3939
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
4040
<author homepage="https://github.com/GretaD">GretaD</author>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Mail\Migration;
11+
12+
use Closure;
13+
use OCP\DB\ISchemaWrapper;
14+
use OCP\Migration\IOutput;
15+
use OCP\Migration\SimpleMigrationStep;
16+
17+
class Version5006Date20251019000000 extends SimpleMigrationStep {
18+
/**
19+
* @param Closure(): ISchemaWrapper $schemaClosure
20+
*/
21+
#[\Override]
22+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
23+
$schema = $schemaClosure();
24+
25+
if (!$schema->hasTable('mail_attachments')) {
26+
return $schema;
27+
}
28+
29+
$attachments = $schema->getTable('mail_attachments');
30+
31+
// Ensure created_at is NOT NULL and has no default, so app must set it.
32+
if ($attachments->hasColumn('created_at')) {
33+
$attachments->modifyColumn('created_at', [
34+
'default' => null,
35+
]);
36+
}
37+
38+
return $schema;
39+
}
40+
}

lib/Service/Attachment/AttachmentService.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use OCA\Mail\Exception\UploadException;
2323
use OCA\Mail\IMAP\MessageMapper;
2424
use OCP\AppFramework\Db\DoesNotExistException;
25+
use OCP\AppFramework\Utility\ITimeFactory;
2526
use OCP\Files\File;
2627
use OCP\Files\Folder;
2728
use OCP\Files\NotFoundException;
@@ -52,6 +53,8 @@ class AttachmentService implements IAttachmentService {
5253
*/
5354
private $logger;
5455

56+
private ITimeFactory $timeFactory;
57+
5558
/**
5659
* @param Folder $userFolder
5760
*/
@@ -60,13 +63,15 @@ public function __construct($userFolder,
6063
AttachmentStorage $storage,
6164
IMailManager $mailManager,
6265
MessageMapper $imapMessageMapper,
63-
LoggerInterface $logger) {
66+
LoggerInterface $logger,
67+
ITimeFactory $timeFactory) {
6468
$this->mapper = $mapper;
6569
$this->storage = $storage;
6670
$this->mailManager = $mailManager;
6771
$this->messageMapper = $imapMessageMapper;
6872
$this->userFolder = $userFolder;
6973
$this->logger = $logger;
74+
$this->timeFactory = $timeFactory;
7075
}
7176

7277
/**
@@ -80,6 +85,8 @@ public function addFile(string $userId, UploadedFile $file): LocalAttachment {
8085
$attachment->setUserId($userId);
8186
$attachment->setFileName($file->getFileName());
8287
$attachment->setMimeType($file->getMimeType());
88+
// set createdAt timestamp for cleanup/retention
89+
$attachment->setCreatedAt($this->timeFactory->getTime());
8390

8491
$persisted = $this->mapper->insert($attachment);
8592
try {
@@ -98,6 +105,8 @@ public function addFileFromString(string $userId, string $name, string $mime, st
98105
$attachment->setUserId($userId);
99106
$attachment->setFileName($name);
100107
$attachment->setMimeType($mime);
108+
// set createdAt timestamp for consistency with uploaded attachments
109+
$attachment->setCreatedAt($this->timeFactory->getTime());
101110

102111
$persisted = $this->mapper->insert($attachment);
103112
try {

tests/Integration/Service/DraftServiceIntegrationTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ protected function setUp(): void {
9696
Server::get(AttachmentStorage::class),
9797
$mailManager,
9898
Server::get(MessageMapper::class),
99-
new NullLogger()
99+
new NullLogger(),
100+
Server::get(ITimeFactory::class)
100101
);
101102
$this->client = $this->getClient($this->account);
102103
$this->mapper = Server::get(LocalMessageMapper::class);

tests/Integration/Service/OutboxServiceIntegrationTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ protected function setUp(): void {
9797
Server::get(AttachmentStorage::class),
9898
$mailManager,
9999
Server::get(\OCA\Mail\IMAP\MessageMapper::class),
100-
new NullLogger()
100+
new NullLogger(),
101+
Server::get(ITimeFactory::class)
101102
);
102103
$this->client = $this->getClient($this->account);
103104
$this->mapper = Server::get(LocalMessageMapper::class);

tests/Unit/Service/Attachment/AttachmentServiceTest.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use OCA\Mail\Service\Attachment\AttachmentStorage;
2525
use OCA\Mail\Service\Attachment\UploadedFile;
2626
use OCP\AppFramework\Db\DoesNotExistException;
27+
use OCP\AppFramework\Utility\ITimeFactory;
2728
use OCP\Files\Folder;
2829
use OCP\Files\NotPermittedException;
2930
use OCP\Share\IAttributes;
@@ -53,6 +54,9 @@ class AttachmentServiceTest extends TestCase {
5354
/** @var MockObject|LoggerInterface */
5455
private $logger;
5556

57+
/** @var ITimeFactory|MockObject */
58+
private $timeFactory;
59+
5660
protected function setUp(): void {
5761
parent::setUp();
5862

@@ -62,14 +66,17 @@ protected function setUp(): void {
6266
$this->messageMapper = $this->createMock(MessageMapper::class);
6367
$this->userFolder = $this->createMock(Folder::class);
6468
$this->logger = $this->createMock(LoggerInterface::class);
69+
$this->timeFactory = $this->createMock(ITimeFactory::class);
70+
$this->timeFactory->method('getTime')->willReturn(123456);
6571

6672
$this->service = new AttachmentService(
6773
$this->userFolder,
6874
$this->mapper,
6975
$this->storage,
7076
$this->mailManager,
7177
$this->messageMapper,
72-
$this->logger
78+
$this->logger,
79+
$this->timeFactory
7380
);
7481
}
7582

@@ -82,6 +89,7 @@ public function testAddFileWithUploadException() {
8289
$attachment = LocalAttachment::fromParams([
8390
'userId' => $userId,
8491
'fileName' => 'cat.jpg',
92+
'createdAt' => 123456,
8593
]);
8694
$persistedAttachment = LocalAttachment::fromParams([
8795
'id' => 123,
@@ -113,7 +121,8 @@ public function testAddFile() {
113121
->willReturn('cat.jpg');
114122
$attachment = LocalAttachment::fromParams([
115123
'userId' => $userId,
116-
'fileName' => 'cat.jpg'
124+
'fileName' => 'cat.jpg',
125+
'createdAt' => 123456
117126
]);
118127
$persistedAttachment = LocalAttachment::fromParams([
119128
'id' => 123,
@@ -138,6 +147,7 @@ public function testAddFileFromStringWithUploadException() {
138147
'userId' => $userId,
139148
'fileName' => 'cat.jpg',
140149
'mimeType' => 'image/jpg',
150+
'createdAt' => 123456,
141151
]);
142152
$persistedAttachment = LocalAttachment::fromParams([
143153
'id' => 123,
@@ -168,6 +178,7 @@ public function testAddFileFromString() {
168178
'userId' => $userId,
169179
'fileName' => 'cat.jpg',
170180
'mimeType' => 'image/jpg',
181+
'createdAt' => 123456,
171182
]);
172183
$persistedAttachment = LocalAttachment::fromParams([
173184
'id' => 123,
@@ -283,6 +294,7 @@ public function testHandleAttachmentsForwardedMessageAttachment(): void {
283294
'userId' => $userId,
284295
'fileName' => 'cat.jpg',
285296
'mimeType' => 'text/plain',
297+
'createdAt' => 123456,
286298
]);
287299
$persistedAttachment = LocalAttachment::fromParams([
288300
'id' => 123,
@@ -334,6 +346,7 @@ public function testHandleAttachmentsForwardedAttachment(): void {
334346
'userId' => $userId,
335347
'fileName' => 'cat.jpg',
336348
'mimeType' => 'text/plain',
349+
'createdAt' => 123456,
337350
]);
338351
$persistedAttachment = LocalAttachment::fromParams([
339352
'id' => 123,
@@ -443,6 +456,7 @@ public function testHandleAttachmentsCloudAttachment(): void {
443456
'userId' => $userId,
444457
'fileName' => 'cat.jpg',
445458
'mimeType' => 'text/plain',
459+
'createdAt' => 123456,
446460
]);
447461
$persistedAttachment = LocalAttachment::fromParams([
448462
'id' => 123,

0 commit comments

Comments
 (0)