From 913a7b80040c3d4277cd4ad851b0c269526e074e Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 16 Jan 2025 16:24:10 +0200 Subject: [PATCH 1/6] Add Dependency index validation --- src/Database/Database.php | 36 ++++++++++ src/Database/Validator/IndexDependency.php | 83 ++++++++++++++++++++++ tests/e2e/Adapter/Base.php | 22 +++++- 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/Database/Validator/IndexDependency.php diff --git a/src/Database/Database.php b/src/Database/Database.php index 19c86dcda..68b277732 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -15,11 +15,13 @@ use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Index as IndexValidator; +use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator; use Utopia\Database\Validator\PartialStructure; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\Queries\Document as DocumentValidator; @@ -1898,6 +1900,18 @@ public function updateAttribute(string $collection, string $id, ?string $type = }, $index['attributes']); } } + + /** + * Check index dependency if we are changing the key + */ + $validator = new IndexDependencyValidator( + $collectionDoc->getAttribute('indexes', []), + $this->adapter->getSupportForCastIndexArray(), + ); + + if (! $validator->isValid($attribute)) { + throw new DependencyException($validator->getDescription()); + } } /** @@ -1998,6 +2012,17 @@ public function deleteAttribute(string $collection, string $id): bool throw new DatabaseException('Cannot delete relationship as an attribute'); } + if ($this->validate) { + $validator = new IndexDependencyValidator( + $collection->getAttribute('indexes', []), + $this->adapter->getSupportForCastIndexArray(), + ); + + if (! $validator->isValid($attribute)) { + throw new DependencyException($validator->getDescription()); + } + } + foreach ($indexes as $indexKey => $index) { $indexAttributes = $index->getAttribute('attributes', []); @@ -2074,6 +2099,17 @@ public function renameAttribute(string $collection, string $old, string $new): b throw new NotFoundException('Attribute not found'); } + if ($this->validate) { + $validator = new IndexDependencyValidator( + $collection->getAttribute('indexes', []), + $this->adapter->getSupportForCastIndexArray(), + ); + + if (! $validator->isValid($attribute)) { + throw new DependencyException($validator->getDescription()); + } + } + $attribute->setAttribute('$id', $new); $attribute->setAttribute('key', $new); diff --git a/src/Database/Validator/IndexDependency.php b/src/Database/Validator/IndexDependency.php new file mode 100644 index 000000000..5c62ad00a --- /dev/null +++ b/src/Database/Validator/IndexDependency.php @@ -0,0 +1,83 @@ + + */ + protected array $indexes; + + public function __construct(array $indexes, bool $castIndexSupport) + { + $this->castIndexSupport = $castIndexSupport; + + $this->indexes = $indexes; + } + + /** + * Returns validator description + */ + public function getDescription(): string + { + return $this->message; + } + + /** + * Is valid. + * + * @param Document $value + */ + public function isValid($value): bool + { + if (! $this->castIndexSupport) { + return true; + } + + if (! $value->getAttribute('array', false)) { + return true; + } + + $key = \strtolower($value->getAttribute('key', $value->getAttribute('$id'))); + + foreach ($this->indexes as $index) { + $attributes = $index->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($key === \strtolower($attribute)) { + return false; + } + } + } + + return true; + } + + /** + * Is array + * + * Function will return true if object is array. + */ + public function isArray(): bool + { + return false; + } + + /** + * Get Type + * + * Returns validator type. + */ + public function getType(): string + { + return self::TYPE_OBJECT; + } +} diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index 040b2005a..3c3e917aa 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -2958,21 +2958,39 @@ public function testArrayAttribute(): void $database->createIndex($collection, 'idx_cards', Database::INDEX_KEY, ['cards'], [100]); if ($this->getDatabase()->getAdapter()->getSupportForCastIndexArray()) { + /** + * Delete attribute + */ try { $database->deleteAttribute($collection, 'cards'); $this->fail('Failed to throw exception'); } catch (Throwable $e) { $this->assertInstanceOf(DependencyException::class, $e); - $this->assertEquals('Attribute cannot be deleted because it is used in an index', $e->getMessage()); + $this->assertEquals('Attribute cannot be renamed or deleted because it is used in an index', $e->getMessage()); } + /** + * Rename attribute + */ try { $database->renameAttribute($collection, 'cards', 'cards_new'); $this->fail('Failed to throw exception'); } catch (Throwable $e) { $this->assertInstanceOf(DependencyException::class, $e); - $this->assertEquals('Attribute cannot be deleted because it is used in an index', $e->getMessage()); + $this->assertEquals('Attribute cannot be renamed or deleted because it is used in an index', $e->getMessage()); } + + /** + * Update attribute + */ + try { + $database->updateAttribute($collection, id:'cards', newKey: 'cards_new'); + $this->fail('Failed to throw exception'); + } catch (Throwable $e) { + $this->assertInstanceOf(DependencyException::class, $e); + $this->assertEquals('Attribute cannot be renamed or deleted because it is used in an index', $e->getMessage()); + } + } else { $this->assertTrue($database->renameAttribute($collection, 'cards', 'cards_new')); $this->assertTrue($database->deleteAttribute($collection, 'cards_new')); From bd704c352db566ceb1bdceada953c12b5c13637f Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 16 Jan 2025 16:27:31 +0200 Subject: [PATCH 2/6] formatting --- src/Database/Database.php | 2 +- src/Database/Validator/IndexDependency.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 68b277732..49ecee7e4 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7,6 +7,7 @@ use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; @@ -15,7 +16,6 @@ use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; -use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; diff --git a/src/Database/Validator/IndexDependency.php b/src/Database/Validator/IndexDependency.php index 5c62ad00a..b806568a8 100644 --- a/src/Database/Validator/IndexDependency.php +++ b/src/Database/Validator/IndexDependency.php @@ -2,7 +2,6 @@ namespace Utopia\Database\Validator; -use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Validator; From 7f433be4596ae3fce0af98e5a02c446aeaff19f2 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 16 Jan 2025 16:43:10 +0200 Subject: [PATCH 3/6] Overwrite --- src/Database/Validator/IndexDependency.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Database/Validator/IndexDependency.php b/src/Database/Validator/IndexDependency.php index b806568a8..12b97d056 100644 --- a/src/Database/Validator/IndexDependency.php +++ b/src/Database/Validator/IndexDependency.php @@ -18,6 +18,7 @@ class IndexDependency extends Validator public function __construct(array $indexes, bool $castIndexSupport) { + $castIndexSupport = true; // Only for testing Appwrite Remove before merge! $this->castIndexSupport = $castIndexSupport; $this->indexes = $indexes; From 536dc3e578990f6aa156675700cc4ec763c16862 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 16 Jan 2025 17:38:32 +0200 Subject: [PATCH 4/6] Remove hardcoded true --- src/Database/Validator/IndexDependency.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Database/Validator/IndexDependency.php b/src/Database/Validator/IndexDependency.php index 12b97d056..13c8a0f6c 100644 --- a/src/Database/Validator/IndexDependency.php +++ b/src/Database/Validator/IndexDependency.php @@ -18,9 +18,7 @@ class IndexDependency extends Validator public function __construct(array $indexes, bool $castIndexSupport) { - $castIndexSupport = true; // Only for testing Appwrite Remove before merge! $this->castIndexSupport = $castIndexSupport; - $this->indexes = $indexes; } From 7494da32376dd1e4c06f9717ab178078fc732bab Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 16 Jan 2025 17:40:37 +0200 Subject: [PATCH 5/6] Fix message --- src/Database/Validator/IndexDependency.php | 2 +- tests/e2e/Adapter/Base.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Database/Validator/IndexDependency.php b/src/Database/Validator/IndexDependency.php index 13c8a0f6c..bac10c7e0 100644 --- a/src/Database/Validator/IndexDependency.php +++ b/src/Database/Validator/IndexDependency.php @@ -7,7 +7,7 @@ class IndexDependency extends Validator { - protected string $message = 'Attribute cannot be renamed or deleted because it is used in an index'; + protected string $message = "Attribute can't be deleted or renamed because it is used in an index"; protected bool $castIndexSupport; diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index 3c3e917aa..ae6ad4653 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -2966,7 +2966,7 @@ public function testArrayAttribute(): void $this->fail('Failed to throw exception'); } catch (Throwable $e) { $this->assertInstanceOf(DependencyException::class, $e); - $this->assertEquals('Attribute cannot be renamed or deleted because it is used in an index', $e->getMessage()); + $this->assertEquals("Attribute can't be deleted or renamed because it is used in an index", $e->getMessage()); } /** @@ -2977,7 +2977,7 @@ public function testArrayAttribute(): void $this->fail('Failed to throw exception'); } catch (Throwable $e) { $this->assertInstanceOf(DependencyException::class, $e); - $this->assertEquals('Attribute cannot be renamed or deleted because it is used in an index', $e->getMessage()); + $this->assertEquals("Attribute can't be deleted or renamed because it is used in an index", $e->getMessage()); } /** @@ -2988,7 +2988,7 @@ public function testArrayAttribute(): void $this->fail('Failed to throw exception'); } catch (Throwable $e) { $this->assertInstanceOf(DependencyException::class, $e); - $this->assertEquals('Attribute cannot be renamed or deleted because it is used in an index', $e->getMessage()); + $this->assertEquals("Attribute can't be deleted or renamed because it is used in an index", $e->getMessage()); } } else { From f1bf1816d26ff0be61772a164695d0f33dbcf1a1 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 16 Jan 2025 17:43:30 +0200 Subject: [PATCH 6/6] fix hint --- src/Database/Validator/IndexDependency.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Database/Validator/IndexDependency.php b/src/Database/Validator/IndexDependency.php index bac10c7e0..7e8453b83 100644 --- a/src/Database/Validator/IndexDependency.php +++ b/src/Database/Validator/IndexDependency.php @@ -16,6 +16,10 @@ class IndexDependency extends Validator */ protected array $indexes; + /** + * @param array $indexes + * @param bool $castIndexSupport + */ public function __construct(array $indexes, bool $castIndexSupport) { $this->castIndexSupport = $castIndexSupport;