From f274807df9ecaa363a466654a993d0d84ca1fbb6 Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Mon, 2 Mar 2020 10:30:49 -0800 Subject: [PATCH 1/7] Ability to pass in a custom authentication url (maintains backwards compat) getResponseData from exception can return an array --- src/Client.php | 43 ++++++++++++++++--------- src/Exceptions/APIResponseException.php | 6 +++- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/Client.php b/src/Client.php index 1aaf062..5aaabe5 100644 --- a/src/Client.php +++ b/src/Client.php @@ -6,6 +6,7 @@ use Classy\Exceptions\SDKException; use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Psr7\Response; class Client { @@ -42,7 +43,8 @@ public function __construct(array $config, $clientClass = GuzzleClient::class) { $config += [ 'base_uri' => 'https://api.classy.org', - 'check_ssl_cert' => true + 'check_ssl_cert' => true, + 'token_endpoint' => '/oauth2/auth' ]; $this->httpClient = new GuzzleClient([ @@ -63,6 +65,7 @@ public function __construct(array $config, $clientClass = GuzzleClient::class) $this->version = urlencode($config['version']); $this->client_id = $config['client_id']; $this->client_secret = $config['client_secret']; + $this->token_endpoint = $config['token_endpoint']; } /** @@ -107,16 +110,18 @@ public function setDefaultSession(Session $session) * @param $code * @return Session */ - public function newMemberSessionFromCode($code) + public function newMemberSessionFromCode($code, $redirectURI = null) { - $response = $this->request('POST', '/oauth2/auth', null, [ + $response = $this->request('POST', $this->token_endpoint, null, [ 'form_params' => [ 'grant_type' => 'authorization_code', 'client_id' => $this->client_id, 'client_secret' => $this->client_secret, - 'code' => $code + 'code' => $code, + 'redirect_uri' => $redirectURI ] ]); + return new Session($response); } @@ -138,7 +143,7 @@ public function newMemberSessionFromCredentials(array $params = []) 'ip' => empty($ips) ? null : implode(', ', $ips), ]); try { - $response = $this->request('POST', '/oauth2/auth', null, [ + $response = $this->request('POST', $this->token_endpoint, null, [ 'form_params' => $params ]); } catch (APIResponseException $e) { @@ -169,7 +174,7 @@ public function refresh(Session $session) { if (!is_null($session->getRefreshToken())) { $ips = $this->getClientIps(); - $response = $this->request('POST', '/oauth2/auth', null, [ + $response = $this->request('POST', $this->token_endpoint, null, [ 'form_params' => [ 'grant_type' => 'refresh_token', 'client_id' => $this->client_id, @@ -179,7 +184,7 @@ public function refresh(Session $session) ] ]); } else { - $response = $this->request('POST', '/oauth2/auth', null, [ + $response = $this->request('POST', $this->token_endpoint, null, [ 'form_params' => [ 'grant_type' => 'client_credentials', 'client_id' => $this->client_id, @@ -245,10 +250,12 @@ public function delete($endpoint, Session $session = null) * @param $endpoint * @param Session|null $session * @param array $options - * @return array + * @param array $config */ - public function request($verb, $endpoint, Session $session = null, $options = []) + public function request($verb, $endpoint, Session $session = null, $options = [], $config = []) { + $config += [ 'results_as' => 'object' ]; + if (is_null($session) && !is_null($this->defaultSession)) { $session = $this->defaultSession; } @@ -264,15 +271,21 @@ public function request($verb, $endpoint, Session $session = null, $options = [] } try { - $content = $this->httpClient - ->request($verb, $endpoint, $options) - ->getBody() - ->getContents(); + + $response = $this->httpClient->request($verb, $endpoint, $options); + + switch($config['results_as']) { + case 'response': + return $response; + case 'array': + return json_decode($response->getBody()->getContents(), true); + default: + return json_decode($response->getBody()->getContents()); + } + } catch (BadResponseException $e) { throw new APIResponseException($e->getMessage(), $e->getCode(), $e); } - - return json_decode($content); } private function applyVersion($version, $endpoint) diff --git a/src/Exceptions/APIResponseException.php b/src/Exceptions/APIResponseException.php index e3a3805..98ce691 100644 --- a/src/Exceptions/APIResponseException.php +++ b/src/Exceptions/APIResponseException.php @@ -10,8 +10,12 @@ public function __construct($message = "", $code = 0, BadResponseException $prev parent::__construct($message, $code, $previous); } - public function getResponseData() + public function getResponseData(bool $assoc = false) { + if ($assoc) { + return json_decode($this->getPrevious()->getResponse()->getBody()->getContents(), true); + } + return json_decode($this->getPrevious()->getResponse()->getBody()->getContents()); } From 172693297a79f492d22fab99b6c03859c713a9c0 Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Mon, 2 Mar 2020 10:34:12 -0800 Subject: [PATCH 2/7] remove type hint --- src/Exceptions/APIResponseException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exceptions/APIResponseException.php b/src/Exceptions/APIResponseException.php index 98ce691..241f802 100644 --- a/src/Exceptions/APIResponseException.php +++ b/src/Exceptions/APIResponseException.php @@ -10,7 +10,7 @@ public function __construct($message = "", $code = 0, BadResponseException $prev parent::__construct($message, $code, $previous); } - public function getResponseData(bool $assoc = false) + public function getResponseData($assoc = false) { if ($assoc) { return json_decode($this->getPrevious()->getResponse()->getBody()->getContents(), true); From 18d7724647fbc5058530d58d2afa08b077fe4de3 Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Mon, 2 Mar 2020 10:36:13 -0800 Subject: [PATCH 3/7] remove uneccessary conditional --- src/Exceptions/APIResponseException.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Exceptions/APIResponseException.php b/src/Exceptions/APIResponseException.php index 241f802..0205f8f 100644 --- a/src/Exceptions/APIResponseException.php +++ b/src/Exceptions/APIResponseException.php @@ -12,11 +12,7 @@ public function __construct($message = "", $code = 0, BadResponseException $prev public function getResponseData($assoc = false) { - if ($assoc) { - return json_decode($this->getPrevious()->getResponse()->getBody()->getContents(), true); - } - - return json_decode($this->getPrevious()->getResponse()->getBody()->getContents()); + return json_decode($this->getPrevious()->getResponse()->getBody()->getContents(), $assoc); } public function getResponseHeaders() From d5e3d96251f0f5fd8e5fbf07a89132372748dbae Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Thu, 19 Mar 2020 08:53:45 -0700 Subject: [PATCH 4/7] change travis dist to trusty for php 5.5 support --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4e4671f..25c8cd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: php +dist: trusty + php: - 5.5 - 5.6 From bd435fbf01bdde7b8f1509a6d522694c0192b8fe Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Thu, 19 Mar 2020 10:24:45 -0700 Subject: [PATCH 5/7] fix tests --- tests/ClientTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 4afec97..1f23a1c 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -90,12 +90,14 @@ public function testNewMemberSessionFromCode() 'grant_type' => 'authorization_code', 'client_id' => '123', 'client_secret' => '456', - 'code' => '789' + 'code' => '789', + 'redirect_uri' => 'https://example.com/callback' ]; })) ->andReturn(new Response(200, [], "{}")); - $session = $this->client->newMemberSessionFromCode("789"); + $session = $this->client->newMemberSessionFromCode("789", 'https://example.com/callback'); + $this->assertInstanceOf(Session::class, $session); } @@ -382,7 +384,7 @@ public function testErrorHandling() $this->fail('Exception expected'); } catch (APIResponseException $e) { $this->assertEquals(400, $e->getCode()); - $this->assertEquals('invalid_request', $e->getResponseData()->error); + $this->assertEquals('invalid_client', $e->getResponseData()->error); $this->assertEquals('application/json; charset=utf-8', $e->getResponseHeaders()['Content-Type'][0]); } } From 8c7a24327b96157ec14df605033ed5ca5184ba03 Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Thu, 19 Mar 2020 11:01:53 -0700 Subject: [PATCH 6/7] test new Exception functionality to return array --- src/Client.php | 20 ++++++----- test.js | 0 tests/ClientTest.php | 85 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 test.js diff --git a/src/Client.php b/src/Client.php index 5aaabe5..d078b08 100644 --- a/src/Client.php +++ b/src/Client.php @@ -112,14 +112,19 @@ public function setDefaultSession(Session $session) */ public function newMemberSessionFromCode($code, $redirectURI = null) { + $formParams = [ + 'grant_type' => 'authorization_code', + 'client_id' => $this->client_id, + 'client_secret' => $this->client_secret, + 'code' => $code + ]; + + if ($redirectURI) { + $formParams['redirect_uri'] = $redirectURI; + } + $response = $this->request('POST', $this->token_endpoint, null, [ - 'form_params' => [ - 'grant_type' => 'authorization_code', - 'client_id' => $this->client_id, - 'client_secret' => $this->client_secret, - 'code' => $code, - 'redirect_uri' => $redirectURI - ] + 'form_params' => $formParams ]); return new Session($response); @@ -295,7 +300,6 @@ private function applyVersion($version, $endpoint) return "/$version/$endpoint"; } - /** * @return array */ diff --git a/test.js b/test.js new file mode 100644 index 0000000..e69de29 diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 1f23a1c..5d7a17b 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -6,7 +6,6 @@ use Classy\Exceptions\APIResponseException; use Classy\Exceptions\SDKException; use Classy\Session; -use GuzzleHttp\Exception\BadResponseException; use GuzzleHttp\Psr7\Response; use Mockery; use ReflectionMethod; @@ -14,7 +13,7 @@ class ClientTest extends TestCase { - public function constructProvider() + public function constructErrorProvider() { return [ [[], "You must define the version of Classy API you want to use"], @@ -25,7 +24,7 @@ public function constructProvider() } /** - * @dataProvider constructProvider + * @dataProvider constructErrorProvider * @covers Classy\Client::__construct */ public function testConstructFailure($inputs, $error) @@ -38,6 +37,24 @@ public function testConstructFailure($inputs, $error) } } + /** + * @covers Classy\Client::__construct + */ + public function testSettingTokenEndpoint() + { + $client = new Client(['version' => '2.0', 'client_id' => '123', 'client_secret' => 'asdf', 'token_endpoint' => '/callback']); + $this->assertEquals($client->token_endpoint, '/callback'); + } + + /** + * @covers Classy\Client::__construct + */ + public function testNotSettingTokenEndpoint() + { + $client = new Client(['version' => '2.0', 'client_id' => '123', 'client_secret' => 'asdf']); + $this->assertEquals($client->token_endpoint, '/oauth2/auth'); + } + /** * @covers Classy\Client::__construct */ @@ -87,12 +104,34 @@ public function testNewMemberSessionFromCode() ->once() ->with('POST', '/oauth2/auth', Mockery::on(function($args) { return $args['form_params'] === [ - 'grant_type' => 'authorization_code', - 'client_id' => '123', - 'client_secret' => '456', - 'code' => '789', - 'redirect_uri' => 'https://example.com/callback' - ]; + 'grant_type' => 'authorization_code', + 'client_id' => '123', + 'client_secret' => '456', + 'code' => '789' + ]; + })) + ->andReturn(new Response(200, [], "{}")); + + $session = $this->client->newMemberSessionFromCode("789"); + + $this->assertInstanceOf(Session::class, $session); + } + + /** + * @covers Classy\Client::newMemberSessionFromCode + */ + public function testNewMemberSessionFromCodeWithRedirectUri() + { + $this->guzzleMock->shouldReceive('request') + ->once() + ->with('POST', '/oauth2/auth', Mockery::on(function($args) { + return $args['form_params'] === [ + 'grant_type' => 'authorization_code', + 'client_id' => '123', + 'client_secret' => '456', + 'code' => '789', + 'redirect_uri' => 'https://example.com/callback' + ]; })) ->andReturn(new Response(200, [], "{}")); @@ -369,9 +408,9 @@ public function testApplyVersion() } /** - * @covers Classy\Client::request - * @covers Classy\Exceptions\APIResponseException - */ + * @covers Classy\Client::request + * @covers Classy\Exceptions\APIResponseException + */ public function testErrorHandling() { $client = new Client([ @@ -388,4 +427,26 @@ public function testErrorHandling() $this->assertEquals('application/json; charset=utf-8', $e->getResponseHeaders()['Content-Type'][0]); } } + + /** + * @covers Classy\Client::request + * @covers Classy\Exceptions\APIResponseException + */ + public function testGetResponseDataCanReturnArray() + { + // passing true to get response data is a proxy to passing true as the second param in json_decode + $assoc = true; + + $client = new Client([ + 'version' => '2.0', + 'client_id' => 'aze', + 'client_secret' => 'aze' + ]); + try { + $client->newAppSession(); + $this->fail('Exception expected'); + } catch (APIResponseException $e) { + $this->assertEquals('invalid_client', $e->getResponseData($assoc)['error']); + } + } } From b531f7a95b8a52841bfc1fb5c4a33d4b956297a0 Mon Sep 17 00:00:00 2001 From: Joey Grisafe Date: Thu, 19 Mar 2020 11:12:08 -0700 Subject: [PATCH 7/7] remove superfluous import --- src/Client.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index d078b08..112401f 100644 --- a/src/Client.php +++ b/src/Client.php @@ -6,7 +6,6 @@ use Classy\Exceptions\SDKException; use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Exception\BadResponseException; -use GuzzleHttp\Psr7\Response; class Client {