diff --git a/doc/index.md b/doc/index.md index 6c3c8152de4..e278e2c931b 100644 --- a/doc/index.md +++ b/doc/index.md @@ -15,6 +15,7 @@ APIs: * [Pull Requests](pull_requests.md) * [Comments](pull_request/comments.md) * [Repositories](repos.md) + * [Contents](repo/contents.md) * [Releases](repo/releases.md) * [Assets](repo/assets.md) * [Users](users.md) diff --git a/doc/repo/contents.md b/doc/repo/contents.md new file mode 100644 index 00000000000..6307f5d449b --- /dev/null +++ b/doc/repo/contents.md @@ -0,0 +1,56 @@ +## Repo / Contents API +[Back to the "Repos API"](../repos.md) | [Back to the navigation](../index.md) + +### Get a repository's README + +```php +$readme = $client->api('repo')->contents()->readme('knp-labs', 'php-github-api', $reference); +``` + +### Get information about a repository file or directory + +```php +$fileInfo = $client->api('repo')->contents()->show('knp-labs', 'php-github-api', $path, $reference); +``` + +### Check that a file or directory exists in the repository +```php +$fileExists = $client->api('repo')->contents()->exists('knp-labs', 'php-github-api', $path, $reference); +``` + +### Create a file +```php +$committer = array('name' => 'KnpLabs', 'email' => 'info@knplabs.com'); + +$fileInfo = $client->api('repo')->contents()->create('knp-labs', 'php-github-api', $path, $content, $commitMessage, $branch, $committer); +``` + +### Update a file + +```php +$committer = array('name' => 'KnpLabs', 'email' => 'info@knplabs.com'); +$oldFile = $client->api('repo')->contents()->show('knp-labs', 'php-github-api', $path, $branch); + +$fileInfo = $client->api('repo')->contents()->create('knp-labs', 'php-github-api', $path, $content, $commitMessage, $oldFile['sha'], $branch, $committer); +``` + +### Remove a file + +```php +$committer = array('name' => 'KnpLabs', 'email' => 'info@knplabs.com'); +$oldFile = $client->api('repo')->contents()->show('knp-labs', 'php-github-api', $path, $branch); + +$fileInfo = $client->api('repo')->contents()->rm('knp-labs', 'php-github-api', $path, $commitMessage, $oldFile['sha'], $branch, $committer); +``` + +### Get repository archive + +```php +// @todo Document +``` + +### Download a file + +```php +$fileContent = $client->api('repo')->contents()->download('knp-labs', 'php-github-api', $path, $reference); +``` diff --git a/lib/Github/Api/AbstractApi.php b/lib/Github/Api/AbstractApi.php index 7ef6e6cf353..724aaf44b35 100644 --- a/lib/Github/Api/AbstractApi.php +++ b/lib/Github/Api/AbstractApi.php @@ -77,6 +77,27 @@ protected function get($path, array $parameters = array(), $requestHeaders = arr return ResponseMediator::getContent($response); } + /** + * Send a HEAD request with query parameters + * + * @param string $path Request path. + * @param array $parameters HEAD parameters. + * @param array $requestHeaders Request headers. + * @return \Guzzle\Http\Message\Response + */ + protected function head($path, array $parameters = array(), $requestHeaders = array()) + { + if (array_key_exists('ref', $parameters) && is_null($parameters['ref'])) { + unset($parameters['ref']); + } + + $response = $this->client->getHttpClient()->request($path, null, 'HEAD', $requestHeaders, array( + 'query' => $parameters + )); + + return $response; + } + /** * Send a POST request with JSON-encoded parameters. * @@ -112,7 +133,6 @@ protected function postRaw($path, $body, $requestHeaders = array()) return ResponseMediator::getContent($response); } - /** * Send a PATCH request with JSON-encoded parameters. * @@ -131,7 +151,6 @@ protected function patch($path, array $parameters = array(), $requestHeaders = a return ResponseMediator::getContent($response); } - /** * Send a PUT request with JSON-encoded parameters. * @@ -150,7 +169,6 @@ protected function put($path, array $parameters = array(), $requestHeaders = arr return ResponseMediator::getContent($response); } - /** * Send a DELETE request with JSON-encoded parameters. * diff --git a/lib/Github/Api/Repository/Contents.php b/lib/Github/Api/Repository/Contents.php index 5ed185b362b..2889c1c7e8b 100644 --- a/lib/Github/Api/Repository/Contents.php +++ b/lib/Github/Api/Repository/Contents.php @@ -6,6 +6,7 @@ use Github\Exception\InvalidArgumentException; use Github\Exception\ErrorException; use Github\Exception\MissingArgumentException; +use Github\Exception\TwoFactorAuthenticationRequiredException; /** * @link http://developer.github.com/v3/repos/contents/ @@ -92,6 +93,40 @@ public function create($username, $repository, $path, $content, $message, $branc return $this->put($url, $parameters); } + /** + * Checks that a given path exists in a repository. + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path of file to check + * @param null|string $reference reference to a branch or commit + * @return boolean + */ + public function exists($username, $repository, $path, $reference = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents'; + + if (null !== $path) { + $url .= '/'.rawurlencode($path); + } + + try { + $response = $this->head($url, array( + 'ref' => $reference + )); + + if ($response->getStatusCode() != 200) { + return false; + } + } catch (TwoFactorAuthenticationRequiredException $ex) { + throw $ex; + } catch (\Exception $ex) { + return false; + } + + return true; + } + /** * Updates the contents of a file in a repository * @link http://developer.github.com/v3/repos/contents/#update-a-file diff --git a/test/Github/Tests/Api/Repository/ContentsTest.php b/test/Github/Tests/Api/Repository/ContentsTest.php index 0f74f237fb8..a57ed3245ac 100644 --- a/test/Github/Tests/Api/Repository/ContentsTest.php +++ b/test/Github/Tests/Api/Repository/ContentsTest.php @@ -3,6 +3,7 @@ namespace Github\Tests\Api\Repository; use Github\Tests\Api\TestCase; +use Github\Exception\TwoFactorAuthenticationRequiredException; class ContentsTest extends TestCase { @@ -38,6 +39,74 @@ public function shouldShowReadme() $this->assertEquals($expectedValue, $api->readme('KnpLabs', 'php-github-api')); } + /** + * @test + */ + public function shouldReturnTrueWhenFileExists() + { + $responseMock = $this->getMockBuilder('\Guzzle\Http\Message\Response') + ->disableOriginalConstructor() + ->getMock(); + + $responseMock->expects($this->any()) + ->method('getStatusCode') + ->willReturn(200); + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('head') + ->with('repos/KnpLabs/php-github-api/contents/composer.json', array('ref' => null)) + ->will($this->returnValue($responseMock)); + + $this->assertEquals(true, $api->exists('KnpLabs', 'php-github-api', 'composer.json')); + } + + public function getFailureStubsForExistsTest() + { + $nonOkResponseMock =$this->getGuzzleResponseMock(); + + $nonOkResponseMock->expects($this->any()) + ->method('getStatusCode') + ->willReturn(403); + + return array( + array($this->throwException(new \ErrorException())), + array($this->returnValue($nonOkResponseMock)) + ); + } + + /** + * @test + * @dataProvider getFailureStubsForExistsTest + */ + public function shouldReturnFalseWhenFileIsNotFound(\PHPUnit_Framework_MockObject_Stub $failureStub) + { + $expectedValue = array('some-header' => 'value'); + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('head') + ->with('repos/KnpLabs/php-github-api/contents/composer.json', array('ref' => null)) + ->will($failureStub); + + $this->assertFalse($api->exists('KnpLabs', 'php-github-api', 'composer.json')); + } + + /** + * @test + * @expectedException \Github\Exception\TwoFactorAuthenticationRequiredException + */ + public function shouldBubbleTwoFactorAuthenticationRequiredExceptionsWhenCheckingFileRequiringAuth() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('head') + ->with('repos/KnpLabs/php-github-api/contents/composer.json', array('ref' => null)) + ->will($this->throwException(new TwoFactorAuthenticationRequiredException(0))); + + $api->exists('KnpLabs', 'php-github-api', 'composer.json'); + } + /** * @test */ @@ -246,4 +315,14 @@ protected function getApiClass() { return 'Github\Api\Repository\Contents'; } + + + private function getGuzzleResponseMock() + { + $responseMock = $this->getMockBuilder('\Guzzle\Http\Message\Response') + ->disableOriginalConstructor() + ->getMock(); + + return $responseMock; + } } diff --git a/test/Github/Tests/Api/TestCase.php b/test/Github/Tests/Api/TestCase.php index b96f0e43bbb..a8f322e31e7 100644 --- a/test/Github/Tests/Api/TestCase.php +++ b/test/Github/Tests/Api/TestCase.php @@ -19,7 +19,7 @@ protected function getApiMock() $client->setHttpClient($mock); return $this->getMockBuilder($this->getApiClass()) - ->setMethods(array('get', 'post', 'postRaw', 'patch', 'delete', 'put')) + ->setMethods(array('get', 'post', 'postRaw', 'patch', 'delete', 'put', 'head')) ->setConstructorArgs(array($client)) ->getMock(); }