diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 7ddde43d3..b654b436e 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -13,6 +13,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Exception\Type as TypeException; @@ -1414,7 +1415,11 @@ public function castingBefore(Document $collection, Document $document): Documen switch ($type) { case Database::VAR_DATETIME: if (!($node instanceof UTCDateTime)) { - $node = new UTCDateTime(new \DateTime($node)); + try { + $node = new UTCDateTime(new \DateTime($node)); + } catch (\Throwable $e) { + throw new StructureException('Invalid datetime value for attribute "' . $key . '": ' . $e->getMessage()); + } } break; case Database::VAR_OBJECT: diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index dd91cc0bd..49d75e4b6 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -5801,6 +5801,64 @@ public function testDateTimeDocument(): void $database->deleteCollection($collection); } + public function testInvalidCreatedAndUpdatedAtThrowStructureException(): void + { + /** @var Database $database */ + $database = $this->getDatabase(); + + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->expectNotToPerformAssertions(); + return; + } + + $collection = 'invalid_date_attributes'; + + $database->createCollection($collection); + $this->assertEquals(true, $database->createAttribute($collection, 'string', Database::VAR_STRING, 128, false)); + + $database->setPreserveDates(true); + + try { + // Outside allowed year range (Structure uses DatetimeValidator min/max, e.g. 0000–9999). + $invalidDate = '10000-01-01T00:00:00.000+00:00'; + + try { + $database->createDocument($collection, new Document([ + '$id' => 'doc1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + '$createdAt' => $invalidDate, + ])); + $this->fail('Expected StructureException for invalid $createdAt'); + } catch (Throwable $e) { + $this->assertInstanceOf(StructureException::class, $e); + } + + $database->createDocument($collection, new Document([ + '$id' => 'doc2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'string' => 'x', + ])); + + try { + $database->updateDocument($collection, 'doc2', new Document([ + '$updatedAt' => $invalidDate, + ])); + $this->fail('Expected StructureException for invalid $updatedAt'); + } catch (Throwable $e) { + $this->assertInstanceOf(StructureException::class, $e); + } + } finally { + $database->setPreserveDates(false); + $database->deleteCollection($collection); + } + } + public function testSingleDocumentDateOperations(): void { /** @var Database $database */