diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index e355aa7..e9f1f8d 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ["8.2", "8.3", "8.4"] + php-versions: ["8.3", "8.4"] steps: - name: Setup PHP diff --git a/composer.json b/composer.json index 6d26c09..deaf4f8 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": ">=8.2", + "php": ">=8.3", "ext-json": "*", "guzzlehttp/guzzle": "^7.10", "symfony/service-contracts": "^3.6", diff --git a/composer.lock b/composer.lock index f4bf221..7e6795b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ff1a2e2e927c70f8b7fcc5ead9598a5e", + "content-hash": "5ebd295a107c4a23f59620b48c7c75ba", "packages": [ { "name": "guzzlehttp/guzzle", @@ -5289,7 +5289,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.2", + "php": ">=8.3", "ext-json": "*" }, "platform-dev": {}, diff --git a/src/Collection/Collection.php b/src/Collection/Collection.php index 87b933c..a5c1dfc 100644 --- a/src/Collection/Collection.php +++ b/src/Collection/Collection.php @@ -136,8 +136,7 @@ class Collection implements JsonSerializable 'type' => 2, 'keyOptions' => [ 'allowUserKeys' => true, - 'type' => 'traditional', - 'lastValue' => 0 + 'type' => KeyType::TRADITIONAL, ], ]; @@ -214,7 +213,7 @@ public function __set(string $name, $value) * @return CursorInterface|bool Cursor if collection exists on database. False otherwise. * @throws GuzzleException|InvalidParameterException|CursorException */ - public function all() + public function all(): bool|CollectionCursor { if (!$this->isNew()) { return new CollectionCursor($this); @@ -272,7 +271,7 @@ public function getName(): string * * @return string|null String if collection exists on database. Null if not. */ - public function getId() + public function getId(): ?string { return ($this->attributes['objectId'] === null) ? $this->attributes['id'] : $this->attributes['objectId']; } diff --git a/src/Collection/KeyType.php b/src/Collection/KeyType.php new file mode 100644 index 0000000..7cd7bdb --- /dev/null +++ b/src/Collection/KeyType.php @@ -0,0 +1,17 @@ + @@ -48,7 +50,33 @@ public function rules(): array 'numberOfShards' => Rules::equalsOrGreaterThan(1), 'isSystem' => Rules::boolean(), 'type' => Rules::in([2, 3]), - 'keyOptions' => Rules::arr(), + 'keyOptions' => Rules::callbackValidation(self::validateKeyOptions()), ]; } + + /** + * Validate key options for collection creation + * + * @return \Closure + */ + private static function validateKeyOptions(): \Closure + { + /** + * 'offset' and 'increment' options are only allowed when used with type 'autoincrement' + * + * @return bool + * @throws InvalidKeyOptionException + */ + return function (array $keyOptions) { + if (array_key_exists('offset', $keyOptions) && $keyOptions['type'] != KeyType::AUTOINCREMENT) { + throw new InvalidKeyOptionException("offset", $keyOptions['type']); + } + + if (array_key_exists('increment', $keyOptions) && $keyOptions['type'] != KeyType::AUTOINCREMENT) { + throw new InvalidKeyOptionException("increment", $keyOptions['type']); + } + + return true; + }; + } } diff --git a/src/Validation/Exceptions/InvalidKeyOptionException.php b/src/Validation/Exceptions/InvalidKeyOptionException.php new file mode 100644 index 0000000..f5d94a2 --- /dev/null +++ b/src/Validation/Exceptions/InvalidKeyOptionException.php @@ -0,0 +1,44 @@ +getIndexes(); - // Try to drop an non-existent index + // Try to drop a non-existent index $this->expectException(DatabaseException::class); $collection->dropIndex($list->last()); } @@ -520,6 +521,24 @@ public function testSave() $this->assertTrue($collection->drop()); } + public function testSaveWithNonDefaultOptions() + { + $keyOptions = [ + 'allowUserKeys' => false, + 'type' => KeyType::UUID, + ]; + + $db = new Database($this->getConnectionObject()); + $collection = new Collection('test_save_coll', $db, ['keyOptions' => $keyOptions]); + + // Check if collection is created. + $this->assertNull($collection->getId()); + + $this->assertTrue($collection->save()); + $this->assertIsString($collection->getId()); + $this->assertTrue($collection->drop()); + } + public function testSaveThrowDatabaseException() { // Mock error diff --git a/tests/Validation/Collection/CollectionValidatorTest.php b/tests/Validation/Collection/CollectionValidatorTest.php index 34086a1..26e5929 100644 --- a/tests/Validation/Collection/CollectionValidatorTest.php +++ b/tests/Validation/Collection/CollectionValidatorTest.php @@ -3,6 +3,8 @@ namespace Unit\Validation\Collection; use Unit\TestCase; +use ArangoDB\Collection\KeyType; +use ArangoDB\Validation\Exceptions\InvalidKeyOptionException; use ArangoDB\Validation\Collection\CollectionValidator; use ArangoDB\Validation\Exceptions\MissingParameterException; use ArangoDB\Validation\Exceptions\InvalidParameterException; @@ -23,7 +25,7 @@ protected function mockCollectionArray(): array 'type' => rand(2, 3), 'keyOptions' => [ 'allowUserKeys' => (bool)rand(0, 1), - 'type' => 'traditional', + 'type' => KeyType::AUTOINCREMENT, 'lastValue' => 0 ], ]; @@ -80,4 +82,53 @@ public function testThrowInvalidParameterException() $this->expectException(InvalidParameterException::class); $this->assertTrue($collectionValidator->validate()); } + + public function testThrowInvalidKeyOptionException() + { + $mock = $this->mockCollectionArray(); + $mock['keyOptions'] = [ + 'allowUserKeys' => (bool)rand(0, 1), + 'type' => KeyType::UUID, + 'offset' => 1, + 'lastValue' => 0 + ]; + $collectionValidator = new CollectionValidator($mock); + $this->expectException(InvalidKeyOptionException::class); + $this->assertTrue($collectionValidator->validate()); + + $mock = $this->mockCollectionArray(); + $mock['keyOptions'] = [ + 'allowUserKeys' => (bool)rand(0, 1), + 'type' => KeyType::TRADITIONAL, + 'offset' => 1, + 'lastValue' => 0 + ]; + $collectionValidator = new CollectionValidator($mock); + $this->expectException(InvalidKeyOptionException::class); + $this->assertTrue($collectionValidator->validate()); + + $mock = $this->mockCollectionArray(); + $mock['keyOptions'] = [ + 'allowUserKeys' => (bool)rand(0, 1), + 'type' => KeyType::PADDED, + 'offset' => 1, + 'lastValue' => 0 + ]; + $collectionValidator = new CollectionValidator($mock); + $this->expectException(InvalidKeyOptionException::class); + $this->assertTrue($collectionValidator->validate()); + } + + public function testAllowExtraOptionsForAutoIncrementKeyType() + { + $mock = $this->mockCollectionArray(); + $mock['keyOptions'] = [ + 'allowUserKeys' => (bool)rand(0, 1), + 'type' => KeyType::AUTOINCREMENT, + 'offset' => 1, + 'lastValue' => 0 + ]; + $collectionValidator = new CollectionValidator($mock); + $this->assertTrue($collectionValidator->validate()); + } }