diff --git a/composer.json b/composer.json index 3b5c7c4..fcddb5d 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,13 @@ "intercom" ], "require": { - "php": "^5.6|^7.0", - "league/oauth2-client": "~2.3" + "php": "^8.0", + "league/oauth2-client": "^2.6" }, "require-dev": { - "phpunit/phpunit": "~4.0", - "mockery/mockery": "~0.9", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^9.5", + "mockery/mockery": "^1.5", + "squizlabs/php_codesniffer": "^3.6" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d737314..c73c759 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,6 @@ - + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> + + + ./ + + + ./vendor + ./tests + + ./tests/ - - - ./ - - ./vendor - ./tests - - - diff --git a/src/Provider/Intercom.php b/src/Provider/Intercom.php index 597cdaf..86bfb0f 100644 --- a/src/Provider/Intercom.php +++ b/src/Provider/Intercom.php @@ -2,25 +2,26 @@ namespace Intercom\OAuth2\Client\Provider; +use JetBrains\PhpStorm\ArrayShape; use League\OAuth2\Client\Provider\AbstractProvider; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; +use League\OAuth2\Client\Provider\ResourceOwnerInterface; use League\OAuth2\Client\Token\AccessToken; use Psr\Http\Message\ResponseInterface; class Intercom extends AbstractProvider { - /** - * @var boolean By default Intercom strategy rejects users with unverified email addresses. + * @var boolean By default, Intercom strategy rejects users with unverified email addresses. */ - protected $verifyEmail = true; + protected bool $verifyEmail = true; /** * Get authorization url to begin OAuth flow * * @return string */ - public function getBaseAuthorizationUrl() + public function getBaseAuthorizationUrl(): string { return 'https://app.intercom.com/oauth'; } @@ -28,9 +29,10 @@ public function getBaseAuthorizationUrl() /** * Get access token url to retrieve token * + * @param array $params * @return string */ - public function getBaseAccessTokenUrl(array $params) + public function getBaseAccessTokenUrl(array $params): string { return 'https://api.intercom.io/auth/eagle/token'; } @@ -42,7 +44,7 @@ public function getBaseAccessTokenUrl(array $params) * * @return string */ - public function getResourceOwnerDetailsUrl(AccessToken $token) + public function getResourceOwnerDetailsUrl(AccessToken $token): string { return 'https://api.intercom.io/me'; } @@ -55,18 +57,19 @@ public function getResourceOwnerDetailsUrl(AccessToken $token) * * @return array */ - protected function getDefaultScopes() + protected function getDefaultScopes(): array { return []; } /** * Check a provider response for errors. + * @throws IdentityProviderException */ - protected function checkResponse(ResponseInterface $response, $data) + protected function checkResponse(ResponseInterface $response, $data): void { $statusCode = $response->getStatusCode(); - if (empty($data['errors']) && $statusCode == 200) { + if (empty($data['errors']) && $statusCode === 200) { return; } @@ -84,7 +87,8 @@ protected function checkResponse(ResponseInterface $response, $data) * * @return array */ - protected function getDefaultHeaders() + #[ArrayShape(['Accept' => "string", 'User-Agent' => "string"])] + protected function getDefaultHeaders(): array { return [ 'Accept' => 'application/json', 'User-Agent' => 'league/oauth2-intercom/2.0.0' ]; } @@ -104,10 +108,11 @@ protected function getAuthorizationHeaders($token = null) /** * Requests resource owner details. * - * @param AccessToken $token + * @param AccessToken $token * @return mixed + * @throws IdentityProviderException */ - protected function fetchResourceOwnerDetails(AccessToken $token) + protected function fetchResourceOwnerDetails(AccessToken $token): mixed { $url = $this->getResourceOwnerDetailsUrl($token); @@ -119,14 +124,16 @@ protected function fetchResourceOwnerDetails(AccessToken $token) /** * Generate a user object from a successful user details request. * - * @param object $response + * @param array $response * @param AccessToken $token - * @return League\OAuth2\Client\Provider\ResourceOwnerInterface + * @return IntercomResourceOwner|ResourceOwnerInterface */ - protected function createResourceOwner(array $response, AccessToken $token) - { + protected function createResourceOwner( + array $response, + AccessToken $token + ): IntercomResourceOwner|ResourceOwnerInterface { $validatedResponse = $response; - if ($this->verifyEmail == true && $response['email_verified'] != true) { + if ($this->verifyEmail && !$response['email_verified']) { $validatedResponse = []; } return new IntercomResourceOwner($validatedResponse); diff --git a/tests/src/Provider/IntercomTest.php b/tests/src/Provider/IntercomTest.php index 8c4a11b..38d036f 100644 --- a/tests/src/Provider/IntercomTest.php +++ b/tests/src/Provider/IntercomTest.php @@ -2,28 +2,33 @@ namespace Intercom\OAuth2\Client\Test\Provider; +use Intercom\OAuth2\Client\Provider\Intercom; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Mockery as m; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\ClientInterface; -class IntercomTest extends \PHPUnit_Framework_TestCase +class IntercomTest extends TestCase { - protected $provider; + protected Intercom $provider; - protected function setUp() + protected function setUp(): void { - $this->provider = new \Intercom\OAuth2\Client\Provider\Intercom([ + $this->provider = new Intercom([ 'clientId' => 'mock_client_id', 'clientSecret' => 'mock_secret', 'redirectUri' => 'none', ]); } - public function tearDown() + public function tearDown(): void { m::close(); parent::tearDown(); } - public function testAuthorizationUrl() + public function testAuthorizationUrl(): void { $url = $this->provider->getAuthorizationUrl(['approval_prompt' => []]); $uri = parse_url($url); @@ -38,32 +43,37 @@ public function testAuthorizationUrl() $this->assertNotNull($this->provider->getState()); } - public function testGetAuthorizationUrl() + public function testGetAuthorizationUrl(): void { $url = $this->provider->getAuthorizationUrl(); - $this->assertContains('https://app.intercom.com/oauth', $url); + $this->assertStringContainsString('https://app.intercom.com/oauth', $url); } - public function testGetBaseAccessTokenUrl() + public function testGetBaseAccessTokenUrl(): void { $url = $this->provider->getBaseAccessTokenUrl([]); - $this->assertContains('https://api.intercom.io/auth/eagle/token', $url); + $this->assertStringContainsString('https://api.intercom.io/auth/eagle/token', $url); } - public function getResourceOwnerDetailsUrl() + public function getResourceOwnerDetailsUrl(): void { - $url = $this->provider->getBaseAccessTokenUrl('mock_token'); - $this->assertContains('https://api.intercom.io/me', $url); + $url = $this->provider->getBaseAccessTokenUrl((array)'mock_token'); + $this->assertStringContainsString('https://api.intercom.io/me', $url); } - public function testGetAccessToken() + /** + * @throws IdentityProviderException + */ + public function testGetAccessToken(): void { - $response = m::mock('Psr\Http\Message\ResponseInterface'); - $response->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token", "token_type": "bearer", "uid": "12345"}'); + $response = m::mock(ResponseInterface::class); + $response->shouldReceive('getBody')->andReturn( + '{"access_token": "mock_access_token", "token_type": "bearer", "uid": "12345"}' + ); $response->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $response->shouldReceive('getStatusCode')->andReturn(200); - $client = m::mock('GuzzleHttp\ClientInterface'); + $client = m::mock(ClientInterface::class); $client->shouldReceive('send')->times(1)->andReturn($response); $this->provider->setHttpClient($client); @@ -79,11 +89,29 @@ public function testUserData() { $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); - $postResponse->shouldReceive('getBody')->andReturn('{"token": "mock_access_token", "access_token": "mock_access_token", "token_type": "Bearer"}'); + $postResponse->shouldReceive('getBody')->andReturn( + '{"token": "mock_access_token", "access_token": "mock_access_token", "token_type": "Bearer"}' + ); $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $postResponse->shouldReceive('getStatusCode')->andReturn(200); - $userJson = '{"type":"admin","id":"368312","email":"fizbit@intercom.io","name":"Fizbit Grappleboot","email_verified":true,"app":{"type":"app","id_code":"2qmk5gy1","created_at":1358214715,"secure":true},"avatar":{"type":"avatar", "image_url":"https://static.intercomassets.com/avatars/228311/square_128/1462489937.jpg"}}'; + $userJson = '{ + "type":"admin", + "id":"368312", + "email":"fizbit@intercom.io", + "name":"Fizbit Grappleboot", + "email_verified":true, + "app":{ + "type":"app", + "id_code":"2qmk5gy1", + "created_at":1358214715, + "secure":true + }, + "avatar":{ + "type":"avatar", + "image_url":"https://static.intercomassets.com/avatars/228311/square_128/1462489937.jpg" + } + }'; $userArray = json_decode($userJson, true); $userResponse = m::mock('Psr\Http\Message\ResponseInterface'); @@ -91,7 +119,7 @@ public function testUserData() $userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $userResponse->shouldReceive('getStatusCode')->andReturn(200); - $client = m::mock('GuzzleHttp\ClientInterface'); + $client = m::mock(ClientInterface::class); $client->shouldReceive('send') ->times(2) ->andReturn($postResponse, $userResponse); @@ -107,15 +135,33 @@ public function testUserData() $this->assertEquals($userArray['avatar']['image_url'], $user->getAvatarUrl()); } - public function testUserUnverifiedEmail() + public function testUserUnverifiedEmail(): void { $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); - $postResponse->shouldReceive('getBody')->andReturn('{"token": "mock_access_token", "access_token": "mock_access_token", "token_type": "Bearer"}'); + $postResponse->shouldReceive('getBody')->andReturn( + '{"token": "mock_access_token", "access_token": "mock_access_token", "token_type": "Bearer"}' + ); $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $postResponse->shouldReceive('getStatusCode')->andReturn(200); - $userJson = '{"type":"admin","id":"368312","email":"fizbit@intercom.io","name":"Fizbit Grappleboot","email_verified":false,"app":{"type":"app","id_code":"2qmk5gy1","created_at":1358214715,"secure":true},"avatar":{"type":"avatar", "image_url":"https://static.intercomassets.com/avatars/228311/square_128/1462489937.jpg"}}'; + $userJson = '{ + "type":"admin", + "id":"368312", + "email":"fizbit@intercom.io", + "name":"Fizbit Grappleboot", + "email_verified":false, + "app":{ + "type":"app", + "id_code":"2qmk5gy1", + "created_at":1358214715, + "secure":true + }, + "avatar":{ + "type":"avatar", + "image_url":"https://static.intercomassets.com/avatars/228311/square_128/1462489937.jpg" + } + }'; $userArray = json_decode($userJson, true); $userResponse = m::mock('Psr\Http\Message\ResponseInterface'); @@ -123,7 +169,7 @@ public function testUserUnverifiedEmail() $userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $userResponse->shouldReceive('getStatusCode')->andReturn(200); - $client = m::mock('GuzzleHttp\ClientInterface'); + $client = m::mock(ClientInterface::class); $client->shouldReceive('send') ->times(2) ->andReturn($postResponse, $userResponse); @@ -132,29 +178,44 @@ public function testUserUnverifiedEmail() $token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']); $user = $this->provider->getResourceOwner($token); - $this->assertEquals(array(), $user->toArray()); + $this->assertEquals([], $user->toArray()); $this->assertEquals(null, $user->getId()); $this->assertEquals(null, $user->getEmail()); $this->assertEquals(null, $user->getName()); $this->assertEquals(null, $user->getAvatarUrl()); } - public function testSkipUserUnverifiedEmailCheck() + public function testSkipUserUnverifiedEmailCheck(): void { - $provider = new \Intercom\OAuth2\Client\Provider\Intercom([ + $provider = new Intercom([ 'clientId' => 'mock_client_id', 'clientSecret' => 'mock_secret', 'redirectUri' => 'none', - 'verifyEmail' => false + 'verifyEmail' => false, ]); $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); - $postResponse->shouldReceive('getBody')->andReturn('{"token": "mock_access_token", "access_token": "mock_access_token", "token_type": "Bearer"}'); + $postResponse->shouldReceive('getBody')->andReturn('{ + "token": "mock_access_token", + "access_token": "mock_access_token", + "token_type": "Bearer" + }'); $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $postResponse->shouldReceive('getStatusCode')->andReturn(200); - $userJson = '{"type":"admin","id":"368312","email":"fizbit@intercom.io","name":"Fizbit Grappleboot","email_verified":false,"app":{"type":"app","id_code":"2qmk5gy1","created_at":1358214715,"secure":true},"avatar":{"type":"avatar", "image_url":"https://static.intercomassets.com/avatars/228311/square_128/1462489937.jpg"}}'; + $userJson = '{ + "type":"admin", + "id":"368312", + "email":"fizbit@intercom.io", + "name":"Fizbit Grappleboot", + "email_verified":false, + "app":{"type":"app","id_code":"2qmk5gy1","created_at":1358214715,"secure":true}, + "avatar":{ + "type":"avatar", + "image_url":"https://static.intercomassets.com/avatars/228311/square_128/1462489937.jpg" + } + }'; $userArray = json_decode($userJson, true); $userResponse = m::mock('Psr\Http\Message\ResponseInterface'); @@ -162,7 +223,7 @@ public function testSkipUserUnverifiedEmailCheck() $userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $userResponse->shouldReceive('getStatusCode')->andReturn(200); - $client = m::mock('GuzzleHttp\ClientInterface'); + $client = m::mock(ClientInterface::class); $client->shouldReceive('send') ->times(2) ->andReturn($postResponse, $userResponse); @@ -179,19 +240,24 @@ public function testSkipUserUnverifiedEmailCheck() } /** - * @expectedException League\OAuth2\Client\Provider\Exception\IdentityProviderException - **/ - public function testExceptionThrownWhenErrorResponse() + * @throws IdentityProviderException + */ + public function testExceptionThrownWhenErrorResponse(): void { - $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); + $this->expectException(IdentityProviderException::class); + $postResponse = m::mock(ResponseInterface::class); - $errorBody = '{"type":"error.list","request_id":"anvt4on87prigma30i8g","errors":[{"code":"server_error","message":"Server Error"}]}'; + $errorBody = '{ + "type":"error.list", + "request_id":"anvt4on87prigma30i8g", + "errors":[{"code":"server_error","message":"Server Error"}] + }'; $postResponse->shouldReceive('getBody')->andReturn($errorBody); $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $postResponse->shouldReceive('getStatusCode')->andReturn(401); - $client = m::mock('GuzzleHttp\ClientInterface'); + $client = m::mock(ClientInterface::class); $client->shouldReceive('send') ->times(1) ->andReturn($postResponse); @@ -200,20 +266,25 @@ public function testExceptionThrownWhenErrorResponse() } /** - * @expectedException League\OAuth2\Client\Provider\Exception\IdentityProviderException - **/ - public function testExceptionThrownWhenStatusNotSuccess() + * @throws IdentityProviderException + */ + public function testExceptionThrownWhenStatusNotSuccess(): void { - $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); + $this->expectException(IdentityProviderException::class); + $postResponse = m::mock(ResponseInterface::class); - $errorBody = '{"type":"error.list","request_id":"anvt4on87prigma30i8g","errors":[{"code":"server_error","message":"Server Error"}]}'; + $errorBody = '{ + "type":"error.list", + "request_id":"anvt4on87prigma30i8g", + "errors":[{"code":"server_error","message":"Server Error"}] + }'; $postResponse->shouldReceive('getBody')->andReturn($errorBody); $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); $postResponse->shouldReceive('getReasonPhrase')->andReturn('Internal Server Error'); $postResponse->shouldReceive('getStatusCode')->andReturn(500); - $client = m::mock('GuzzleHttp\ClientInterface'); + $client = m::mock(ClientInterface::class); $client->shouldReceive('send') ->times(1) ->andReturn($postResponse);