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 4082f1ac..0b5d759d 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; @@ -400,6 +401,14 @@ public function users(): Users return new Users($this); } + /** + * @return PersonalAccessTokens + */ + public function personal_access_tokens(): PersonalAccessTokens + { + return new PersonalAccessTokens($this); + } + /** * @return Version */ 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()); + } + +}