From 0da32e7e976e2206fa31c2cfd11c7104d228cb41 Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Mon, 24 Feb 2025 11:45:21 +0100 Subject: [PATCH 1/8] Recreate PR 797. --- src/Api/PersonalAccessTokens.php | 153 ++++++++++++++++++++++ src/Client.php | 6 + tests/Api/PersonalAccessTokensTest.php | 167 +++++++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 src/Api/PersonalAccessTokens.php create mode 100644 tests/Api/PersonalAccessTokensTest.php diff --git a/src/Api/PersonalAccessTokens.php b/src/Api/PersonalAccessTokens.php new file mode 100644 index 00000000..d42cbaac --- /dev/null +++ b/src/Api/PersonalAccessTokens.php @@ -0,0 +1,153 @@ + +* (c) Graham Campbell +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Gitlab\Api; + +use Symfony\Component\OptionsResolver\Options; + +class PersonalAccessTokens extends AbstractApi +{ + /** + * @param array $parameters { + * + * @var string $search search text + * @var string $state state of the token + * @var int $user_id tokens belonging to the given user + * @var bool $revoked whether the token is revoked or not + * @var \DateTimeInterface $created_before return tokens created before the given time (inclusive) + * @var \DateTimeInterface $created_after return tokens created after the given time (inclusive) + * @var \DateTimeInterface $last_used_after return tokens used before the given time (inclusive) + * @var \DateTimeInterface $last_used_before return tokens used after the given time (inclusive) + * } + * + * @return mixed + */ + public function all(array $parameters = []) + { + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string { + return $value->format('c'); + }; + $booleanNormalizer = function (Options $resolver, $value): string { + return $value ? 'true' : 'false'; + }; + + $resolver->setDefined('search'); + $resolver->setDefined('state') + ->setAllowedValues('state', ['all', 'active', 'inactive']); + $resolver->setDefined('user_id') + ->setAllowedTypes('user_id', 'int') + ->setAllowedValues('user_id', function ($value): bool { + return $value > 0; + }) + ; + $resolver->setDefined('created_before') + ->setAllowedTypes('created_before', \DateTimeInterface::class) + ->setNormalizer('created_before', $datetimeNormalizer) + ; + $resolver->setDefined('created_after') + ->setAllowedTypes('created_after', \DateTimeInterface::class) + ->setNormalizer('created_after', $datetimeNormalizer) + ; + $resolver->setDefined('last_used_after') + ->setAllowedTypes('last_used_after', \DateTimeInterface::class) + ->setNormalizer('last_used_after', $datetimeNormalizer) + ; + $resolver->setDefined('last_used_before') + ->setAllowedTypes('last_used_before', \DateTimeInterface::class) + ->setNormalizer('last_used_before', $datetimeNormalizer) + ; + $resolver->setDefined('revoked') + ->setAllowedTypes('revoked', 'bool') + ->setNormalizer('revoked', $booleanNormalizer); + ; + + return $this->get('personal_access_tokens', $resolver->resolve($parameters)); + } + + /** + * @param int $id + * + * @return mixed + */ + public function show(int $id) + { + return $this->get('personal_access_tokens/'.self::encodePath($id)); + } + + /** + * @return mixed + */ + public function current() + { + return $this->get('personal_access_tokens/self'); + } + + + /** + * @param int $id + * + * @param array $params + * + * @return mixed + */ + public function rotate(int $id, array $params = []) + { + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string { + return $value->format('c'); + }; + $resolver->setDefined('expires_at') + ->setAllowedTypes('expires_at', \DateTimeInterface::class) + ->setNormalizer('expires_at', $datetimeNormalizer) + ; + return $this->post('personal_access_tokens/'.self::encodePath($id).'/rotate', $resolver->resolve($params)); + } + + /** + * @param array $params + * + * @return mixed + */ + public function rotateCurrent(array $params = []) + { + $resolver = $this->createOptionsResolver(); + $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string { + return $value->format('c'); + }; + $resolver->setDefined('expires_at') + ->setAllowedTypes('expires_at', \DateTimeInterface::class) + ->setNormalizer('expires_at', $datetimeNormalizer) + ; + return $this->post('personal_access_tokens/self/rotate', $resolver->resolve($params)); + } + + /** + * @param int $id + * + * @return mixed + */ + public function remove(int $id) + { + return $this->delete('personal_access_tokens/'.self::encodePath($id)); + } + + /** + * @return mixed + */ + public function removeCurrent() + { + return $this->delete('personal_access_tokens/self'); + } +} diff --git a/src/Client.php b/src/Client.php index ee4ff8a7..985277cc 100644 --- a/src/Client.php +++ b/src/Client.php @@ -30,6 +30,7 @@ use Gitlab\Api\Keys; use Gitlab\Api\MergeRequests; use Gitlab\Api\Milestones; +use Gitlab\Api\PersonalAccessTokens; use Gitlab\Api\ProjectNamespaces; use Gitlab\Api\Projects; use Gitlab\Api\Repositories; @@ -240,6 +241,11 @@ public function namespaces(): ProjectNamespaces return new ProjectNamespaces($this); } + public function personal_access_tokens(): PersonalAccessTokens + { + return new PersonalAccessTokens($this); + } + public function projects(): Projects { return new Projects($this); diff --git a/tests/Api/PersonalAccessTokensTest.php b/tests/Api/PersonalAccessTokensTest.php new file mode 100644 index 00000000..b3bde36b --- /dev/null +++ b/tests/Api/PersonalAccessTokensTest.php @@ -0,0 +1,167 @@ + + * (c) Graham Campbell + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Gitlab\Tests\Api; + +use Gitlab\Api\PersonalAccessTokens; + +class PersonalAccessTokensTest extends TestCase +{ + protected function getApiClass() + { + return PersonalAccessTokens::class; + } + + /** + * @test + */ + public function shouldGetAllTokens(): void + { + $expectedArray = [ + ['id' => 1, 'name' => 'Token 1', 'state' => 'active'], + ['id' => 2, 'name' => 'Token 2', 'state' => 'revoked'], + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('personal_access_tokens', []) + ->will($this->returnValue($expectedArray)) + ; + + $this->assertEquals($expectedArray, $api->all()); + } + + /** + * @test + */ + public function shouldGetActiveTokens(): void + { + $expectedArray = [ + ['id' => 1, 'name' => 'Token 1', 'state' => 'active'], + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('personal_access_tokens', ['state' => 'active']) + ->will($this->returnValue($expectedArray)) + ; + + $this->assertEquals($expectedArray, $api->all(['state' => 'active'])); + } + + /** + * @test + */ + public function shouldShowToken(): void + { + $expectedArray = ['id' => 1, 'name' => 'Token 1', 'state' => 'active']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('personal_access_tokens/1') + ->will($this->returnValue($expectedArray)) + ; + + $this->assertEquals($expectedArray, $api->show(1)); + } + + /** + * @test + */ + public function shouldShowCurrent(): void + { + $expectedArray = ['id' => 1, 'name' => 'Token 1', 'state' => 'active']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('personal_access_tokens/self') + ->will($this->returnValue($expectedArray)) + ; + + $this->assertEquals($expectedArray, $api->current()); + } + + /** + * @test + */ + public function shouldRotate(): void + { + $expectedArray = ['id' => 4, 'name' => 'Token 4']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('personal_access_tokens/3/rotate') + ->will($this->returnValue($expectedArray)) + ; + + $this->assertEquals($expectedArray, $api->rotate(3)); + } + + /** + * @test + */ + public function shouldRotateCurrent(): void + { + $expectedArray = ['id' => 4, 'name' => 'Token 4']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('personal_access_tokens/self/rotate') + ->will($this->returnValue($expectedArray)) + ; + + $this->assertEquals($expectedArray, $api->rotateCurrent()); + } + + /** + * @test + */ + public function shouldRemoveToken(): void + { + $expectedBool = true; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('personal_access_tokens/1') + ->will($this->returnValue($expectedBool)) + ; + + $this->assertEquals($expectedBool, $api->remove(1)); + } + + /** + * @test + */ + public function shouldRemoveCurrentToken(): void + { + $expectedBool = true; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('personal_access_tokens/self') + ->will($this->returnValue($expectedBool)) + ; + + $this->assertEquals($expectedBool, $api->removeCurrent()); + } + +} From 40918e5efd38317637512f82276f3013c887dc0e Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Mon, 24 Feb 2025 11:50:04 +0100 Subject: [PATCH 2/8] Spacing. --- src/Api/PersonalAccessTokens.php | 64 ++++++++++++++------------------ 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/src/Api/PersonalAccessTokens.php b/src/Api/PersonalAccessTokens.php index d42cbaac..e0b6e787 100644 --- a/src/Api/PersonalAccessTokens.php +++ b/src/Api/PersonalAccessTokens.php @@ -19,20 +19,20 @@ class PersonalAccessTokens extends AbstractApi { /** - * @param array $parameters { - * - * @var string $search search text - * @var string $state state of the token - * @var int $user_id tokens belonging to the given user - * @var bool $revoked whether the token is revoked or not - * @var \DateTimeInterface $created_before return tokens created before the given time (inclusive) - * @var \DateTimeInterface $created_after return tokens created after the given time (inclusive) - * @var \DateTimeInterface $last_used_after return tokens used before the given time (inclusive) - * @var \DateTimeInterface $last_used_before return tokens used after the given time (inclusive) - * } - * - * @return mixed - */ + * @param array $parameters { + * + * @var string $search search text + * @var string $state state of the token + * @var int $user_id tokens belonging to the given user + * @var bool $revoked whether the token is revoked or not + * @var \DateTimeInterface $created_before return tokens created before the given time (inclusive) + * @var \DateTimeInterface $created_after return tokens created after the given time (inclusive) + * @var \DateTimeInterface $last_used_after return tokens used before the given time (inclusive) + * @var \DateTimeInterface $last_used_before return tokens used after the given time (inclusive) + * } + * + * @return mixed + */ public function all(array $parameters = []) { $resolver = $this->createOptionsResolver(); @@ -70,25 +70,23 @@ public function all(array $parameters = []) ; $resolver->setDefined('revoked') ->setAllowedTypes('revoked', 'bool') - ->setNormalizer('revoked', $booleanNormalizer); + ->setNormalizer('revoked', $booleanNormalizer) ; return $this->get('personal_access_tokens', $resolver->resolve($parameters)); } /** - * @param int $id - * - * @return mixed - */ + * @return mixed + */ public function show(int $id) { return $this->get('personal_access_tokens/'.self::encodePath($id)); } /** - * @return mixed - */ + * @return mixed + */ public function current() { return $this->get('personal_access_tokens/self'); @@ -96,12 +94,8 @@ public function current() /** - * @param int $id - * - * @param array $params - * - * @return mixed - */ + * @return mixed + */ public function rotate(int $id, array $params = []) { $resolver = $this->createOptionsResolver(); @@ -116,10 +110,8 @@ public function rotate(int $id, array $params = []) } /** - * @param array $params - * - * @return mixed - */ + * @return mixed + */ public function rotateCurrent(array $params = []) { $resolver = $this->createOptionsResolver(); @@ -134,18 +126,16 @@ public function rotateCurrent(array $params = []) } /** - * @param int $id - * - * @return mixed - */ + * @return mixed + */ public function remove(int $id) { return $this->delete('personal_access_tokens/'.self::encodePath($id)); } /** - * @return mixed - */ + * @return mixed + */ public function removeCurrent() { return $this->delete('personal_access_tokens/self'); From 576561d138eb50e0b1fd871b850b34f52861309c Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Mon, 24 Feb 2025 11:51:46 +0100 Subject: [PATCH 3/8] Coding standards. --- src/Api/PersonalAccessTokens.php | 3 ++- tests/Api/PersonalAccessTokensTest.php | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Api/PersonalAccessTokens.php b/src/Api/PersonalAccessTokens.php index e0b6e787..88c3e570 100644 --- a/src/Api/PersonalAccessTokens.php +++ b/src/Api/PersonalAccessTokens.php @@ -92,7 +92,6 @@ public function current() return $this->get('personal_access_tokens/self'); } - /** * @return mixed */ @@ -106,6 +105,7 @@ public function rotate(int $id, array $params = []) ->setAllowedTypes('expires_at', \DateTimeInterface::class) ->setNormalizer('expires_at', $datetimeNormalizer) ; + return $this->post('personal_access_tokens/'.self::encodePath($id).'/rotate', $resolver->resolve($params)); } @@ -122,6 +122,7 @@ public function rotateCurrent(array $params = []) ->setAllowedTypes('expires_at', \DateTimeInterface::class) ->setNormalizer('expires_at', $datetimeNormalizer) ; + return $this->post('personal_access_tokens/self/rotate', $resolver->resolve($params)); } diff --git a/tests/Api/PersonalAccessTokensTest.php b/tests/Api/PersonalAccessTokensTest.php index b3bde36b..7e0631f8 100644 --- a/tests/Api/PersonalAccessTokensTest.php +++ b/tests/Api/PersonalAccessTokensTest.php @@ -163,5 +163,4 @@ public function shouldRemoveCurrentToken(): void $this->assertEquals($expectedBool, $api->removeCurrent()); } - } From c8a6d98b5a7b9b71fd27267f39cb56c18b6d0ea6 Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Mon, 24 Feb 2025 11:55:20 +0100 Subject: [PATCH 4/8] Function signature. --- tests/Api/PersonalAccessTokensTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Api/PersonalAccessTokensTest.php b/tests/Api/PersonalAccessTokensTest.php index 7e0631f8..b07356ea 100644 --- a/tests/Api/PersonalAccessTokensTest.php +++ b/tests/Api/PersonalAccessTokensTest.php @@ -18,7 +18,7 @@ class PersonalAccessTokensTest extends TestCase { - protected function getApiClass() + protected function getApiClass(): string { return PersonalAccessTokens::class; } From 61a742eba6b824a9a7752080eed50bd7bda9f3e3 Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Tue, 25 Feb 2025 10:01:29 +0100 Subject: [PATCH 5/8] Test annotation. --- tests/Api/PersonalAccessTokensTest.php | 32 +++++++------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/tests/Api/PersonalAccessTokensTest.php b/tests/Api/PersonalAccessTokensTest.php index b07356ea..4b0c11b0 100644 --- a/tests/Api/PersonalAccessTokensTest.php +++ b/tests/Api/PersonalAccessTokensTest.php @@ -23,9 +23,7 @@ protected function getApiClass(): string return PersonalAccessTokens::class; } - /** - * @test - */ + #[Test] public function shouldGetAllTokens(): void { $expectedArray = [ @@ -43,9 +41,7 @@ public function shouldGetAllTokens(): void $this->assertEquals($expectedArray, $api->all()); } - /** - * @test - */ + #[Test] public function shouldGetActiveTokens(): void { $expectedArray = [ @@ -62,9 +58,7 @@ public function shouldGetActiveTokens(): void $this->assertEquals($expectedArray, $api->all(['state' => 'active'])); } - /** - * @test - */ + #[Test] public function shouldShowToken(): void { $expectedArray = ['id' => 1, 'name' => 'Token 1', 'state' => 'active']; @@ -79,9 +73,7 @@ public function shouldShowToken(): void $this->assertEquals($expectedArray, $api->show(1)); } - /** - * @test - */ + #[Test] public function shouldShowCurrent(): void { $expectedArray = ['id' => 1, 'name' => 'Token 1', 'state' => 'active']; @@ -96,9 +88,7 @@ public function shouldShowCurrent(): void $this->assertEquals($expectedArray, $api->current()); } - /** - * @test - */ + #[Test] public function shouldRotate(): void { $expectedArray = ['id' => 4, 'name' => 'Token 4']; @@ -113,9 +103,7 @@ public function shouldRotate(): void $this->assertEquals($expectedArray, $api->rotate(3)); } - /** - * @test - */ + #[Test] public function shouldRotateCurrent(): void { $expectedArray = ['id' => 4, 'name' => 'Token 4']; @@ -130,9 +118,7 @@ public function shouldRotateCurrent(): void $this->assertEquals($expectedArray, $api->rotateCurrent()); } - /** - * @test - */ + #[Test] public function shouldRemoveToken(): void { $expectedBool = true; @@ -147,9 +133,7 @@ public function shouldRemoveToken(): void $this->assertEquals($expectedBool, $api->remove(1)); } - /** - * @test - */ + #[Test] public function shouldRemoveCurrentToken(): void { $expectedBool = true; From 8eeee25f1633f5063d583bc6fa56eb57d38240fc Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Tue, 25 Feb 2025 10:03:43 +0100 Subject: [PATCH 6/8] Remove unneeded comments. --- src/Api/PersonalAccessTokens.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/Api/PersonalAccessTokens.php b/src/Api/PersonalAccessTokens.php index 88c3e570..e15d98a4 100644 --- a/src/Api/PersonalAccessTokens.php +++ b/src/Api/PersonalAccessTokens.php @@ -30,8 +30,6 @@ class PersonalAccessTokens extends AbstractApi * @var \DateTimeInterface $last_used_after return tokens used before the given time (inclusive) * @var \DateTimeInterface $last_used_before return tokens used after the given time (inclusive) * } - * - * @return mixed */ public function all(array $parameters = []) { @@ -76,25 +74,16 @@ public function all(array $parameters = []) return $this->get('personal_access_tokens', $resolver->resolve($parameters)); } - /** - * @return mixed - */ public function show(int $id) { return $this->get('personal_access_tokens/'.self::encodePath($id)); } - /** - * @return mixed - */ public function current() { return $this->get('personal_access_tokens/self'); } - /** - * @return mixed - */ public function rotate(int $id, array $params = []) { $resolver = $this->createOptionsResolver(); @@ -109,9 +98,6 @@ public function rotate(int $id, array $params = []) return $this->post('personal_access_tokens/'.self::encodePath($id).'/rotate', $resolver->resolve($params)); } - /** - * @return mixed - */ public function rotateCurrent(array $params = []) { $resolver = $this->createOptionsResolver(); @@ -126,17 +112,11 @@ public function rotateCurrent(array $params = []) return $this->post('personal_access_tokens/self/rotate', $resolver->resolve($params)); } - /** - * @return mixed - */ public function remove(int $id) { return $this->delete('personal_access_tokens/'.self::encodePath($id)); } - /** - * @return mixed - */ public function removeCurrent() { return $this->delete('personal_access_tokens/self'); From 32377cecb2208b1f8970a38738cb705ca6749301 Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Tue, 25 Feb 2025 10:06:06 +0100 Subject: [PATCH 7/8] Add return type. --- src/Api/PersonalAccessTokens.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Api/PersonalAccessTokens.php b/src/Api/PersonalAccessTokens.php index e15d98a4..698021ed 100644 --- a/src/Api/PersonalAccessTokens.php +++ b/src/Api/PersonalAccessTokens.php @@ -31,7 +31,7 @@ class PersonalAccessTokens extends AbstractApi * @var \DateTimeInterface $last_used_before return tokens used after the given time (inclusive) * } */ - public function all(array $parameters = []) + public function all(array $parameters = []): mixed { $resolver = $this->createOptionsResolver(); $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string { @@ -74,17 +74,17 @@ public function all(array $parameters = []) return $this->get('personal_access_tokens', $resolver->resolve($parameters)); } - public function show(int $id) + public function show(int $id): mixed { return $this->get('personal_access_tokens/'.self::encodePath($id)); } - public function current() + public function current(): mixed { return $this->get('personal_access_tokens/self'); } - public function rotate(int $id, array $params = []) + public function rotate(int $id, array $params = []): mixed { $resolver = $this->createOptionsResolver(); $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string { @@ -98,7 +98,7 @@ public function rotate(int $id, array $params = []) return $this->post('personal_access_tokens/'.self::encodePath($id).'/rotate', $resolver->resolve($params)); } - public function rotateCurrent(array $params = []) + public function rotateCurrent(array $params = []): mixed { $resolver = $this->createOptionsResolver(); $datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string { @@ -112,12 +112,12 @@ public function rotateCurrent(array $params = []) return $this->post('personal_access_tokens/self/rotate', $resolver->resolve($params)); } - public function remove(int $id) + public function remove(int $id): mixed { return $this->delete('personal_access_tokens/'.self::encodePath($id)); } - public function removeCurrent() + public function removeCurrent(): mixed { return $this->delete('personal_access_tokens/self'); } From 3edb162ecf1fcbcd8a0a74409837b7b95af58114 Mon Sep 17 00:00:00 2001 From: Fran Garcia-Linares Date: Tue, 25 Feb 2025 10:09:12 +0100 Subject: [PATCH 8/8] Add namespace. --- tests/Api/PersonalAccessTokensTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Api/PersonalAccessTokensTest.php b/tests/Api/PersonalAccessTokensTest.php index 4b0c11b0..d8a72e1a 100644 --- a/tests/Api/PersonalAccessTokensTest.php +++ b/tests/Api/PersonalAccessTokensTest.php @@ -15,6 +15,7 @@ namespace Gitlab\Tests\Api; use Gitlab\Api\PersonalAccessTokens; +use PHPUnit\Framework\Attributes\Test; class PersonalAccessTokensTest extends TestCase {