diff --git a/.env.example b/.env.example index 6da69fe..315f2fb 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ # Loads in during testing as we require an API key +HUBSPOT_ACCESS_TOKEN="" # Learn how to get a key here https://knowledge.hubspot.com/articles/kcs_article/integrations/how-do-i-get-my-hubspot-api-key -HUBSPOT_API_KEY="" \ No newline at end of file +HUBSPOT_DEVELOPER_KEY="" \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8a93076..5110782 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.1, 8.0, 7.4] + php: [8.2, 8.1, 8.0] os: [ubuntu-latest] name: PHP${{ matrix.php }} on ${{ matrix.os }} @@ -41,9 +41,8 @@ jobs: - name: Execute CS check env: PHP_CS_FIXER_IGNORE_ENV: 1 - run: vendor/bin/php-cs-fixer fix --dry-run + run: composer lint - name: Execute tests env: - HUBSPOT_API_KEY: ${{secrets.HUBSPOT_PRIVATE_APP_KEY}} - HUBSPOT_USE_OAUTH2: true + HUBSPOT_ACCESS_TOKEN: ${{secrets.HUBSPOT_PRIVATE_APP_KEY}} run: vendor/bin/phpunit \ No newline at end of file diff --git a/.gitignore b/.gitignore index 72bc1ae..f1f6ea7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ composer.lock .DS_Store .env .phpunit.result.cache -.php_cs.cache \ No newline at end of file +.php_cs.cache +.idea +.php-cs-fixer.cache \ No newline at end of file diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 67% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 88cc422..11e2f04 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -2,17 +2,15 @@ $finder = PhpCsFixer\Finder::create() ->exclude('vendor') - ->in(__DIR__) -; + ->in(__DIR__); -return PhpCsFixer\Config::create() +return (new PhpCsFixer\Config) ->setRules([ - '@Symfony' => true, + '@Symfony' => true, '@PSR2' => true, 'array_syntax' => ['syntax' => 'short'], 'php_unit_method_casing' => ['case' => 'snake_case'], ]) - ->setIndent("\t") + ->setIndent("\t") ->setLineEnding("\r\n") - ->setFinder($finder) -; \ No newline at end of file + ->setFinder($finder); \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 69bb19b..1224988 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ php: install: - 'composer install' script: - - 'vendor/bin/php-cs-fixer fix --config=.php_cs.dist -v --dry-run --stop-on-violation --using-cache=no' + - 'vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php -v --dry-run --stop-on-violation --using-cache=no' - 'vendor/bin/phpunit' \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b5d179..6a30af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [6.0.0] - 2023-06-23 + +- Updated to use the new [Hubspot PHP API Client](https://github.com/HubSpot/hubspot-api-php). + ## [5.0.0] - 2022-11-12 - Updated Hubspot package to v5 diff --git a/README.md b/README.md index 5476b54..8527585 100644 --- a/README.md +++ b/README.md @@ -19,36 +19,35 @@ You can use either the facade or inject the HubSpot class as a dependency: ### Facade ```php // Echo all contacts first and last names -$response = HubSpot::contacts()->all(); +$response = HubSpot::crm()->contacts()->basicApi()->getPage(); foreach ($response->contacts as $contact) { echo sprintf( "Contact name is %s %s." . PHP_EOL, - $contact->properties->firstname->value, - $contact->properties->lastname->value + $contact->getProperties()['firstname'], + $contact->getProperties()['lastname'] ); } ``` -### Dependency Injection ```php -Route::get('/', function (Rossjcooper\LaravelHubSpot\HubSpot $hubspot) { - $response = $hubspot->contacts()->all(); +Route::get('/', function (HubSpot\Discovery\Discovery $hubspot) { + $response = $hubspot->crm()->contacts()->basicApi()->getPage(); foreach ($response->contacts as $contact) { echo sprintf( "Contact name is %s %s." . PHP_EOL, - $contact->properties->firstname->value, - $contact->properties->lastname->value + $contact->getProperties()['firstname'], + $contact->getProperties()['lastname'] ); } }); ``` -For more info on using the actual API see the main repo [hubspot/hubspot-php](https://github.com/HubSpot/hubspot-php) +For more info on using the actual API see the main repo [Hubspot/hubspot-api-php](https://github.com/HubSpot/hubspot-api-php) ## Testing -We're using the brilliant [Orchestra Testbench](https://github.com/orchestral/testbench) v4 to run unit tests in a Laravel based environment. If you wish to run tests be sure to have a HubSpot API key inside your `.env` file and run `./vendor/bin/phpunit` +We're using the brilliant [Orchestra Testbench](https://github.com/orchestral/testbench) v4 to run unit tests in a Laravel based environment. If you wish to run tests be sure to have a HubSpot API key inside your `.env` file and run `composer run test` Current unit test access the HubSpot API and expect to see the demo contacts/leads that HubSpot provides to its developer accounts. ## Issues -Please only report issues relating to the Laravel side of things here, main API issues should be reported [here](https://github.com/ryanwinchester/hubspot-php/issues) +Please only report issues relating to the Laravel side of things here, main API issues should be reported [here](https://github.com/HubSpot/hubspot-api-php/issues) diff --git a/VERSION.txt b/VERSION.txt index 28cbf7c..f4965a3 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.0.0 \ No newline at end of file +6.0.0 \ No newline at end of file diff --git a/composer.json b/composer.json index 8ce3489..ba1ed58 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ ], "require": { "illuminate/support": ">=5.3", - "hubspot/hubspot-php": "^5.0" + "hubspot/api-client": "^10.0", + "php": ">=8.0" }, "autoload": { "psr-4": { @@ -37,6 +38,11 @@ ], "require-dev": { "orchestra/testbench": ">=3.4", - "friendsofphp/php-cs-fixer": "^2.15" + "friendsofphp/php-cs-fixer": "^v3.19" + }, + "scripts": { + "test": "./vendor/bin/phpunit", + "lint": "./vendor/bin/php-cs-fixer fix -v --dry-run", + "fix": "./vendor/bin/php-cs-fixer fix" } } diff --git a/phpunit.xml b/phpunit.xml index c1fae83..0d3b2f9 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,28 +1,21 @@ - - - - ./tests/Unit/ - - - ./tests/API/ - - - - - src/ - - - - - - + + + + + ./tests/Unit/ + + + ./tests/API/ + + + + + + + + + src/ + + diff --git a/phpunit.xml.bak b/phpunit.xml.bak new file mode 100644 index 0000000..4ba27f1 --- /dev/null +++ b/phpunit.xml.bak @@ -0,0 +1,28 @@ + + + + + ./tests/Unit/ + + + ./tests/API/ + + + + + src/ + + + + + + + diff --git a/src/Facades/HubSpot.php b/src/Facades/HubSpot.php index 8099e5f..f5602af 100644 --- a/src/Facades/HubSpot.php +++ b/src/Facades/HubSpot.php @@ -2,6 +2,7 @@ namespace Rossjcooper\LaravelHubSpot\Facades; +use HubSpot\Discovery\Discovery; use Illuminate\Support\Facades\Facade; class HubSpot extends Facade @@ -13,6 +14,6 @@ class HubSpot extends Facade */ protected static function getFacadeAccessor() { - return 'Rossjcooper\LaravelHubSpot\HubSpot'; + return Discovery::class; } } diff --git a/src/HubSpot.php b/src/HubSpot.php deleted file mode 100644 index 2196f74..0000000 --- a/src/HubSpot.php +++ /dev/null @@ -1,9 +0,0 @@ -app->bind('Rossjcooper\LaravelHubSpot\HubSpot', function ($app) { - if (env('HUBSPOT_USE_OAUTH2', config('hubspot.use_oauth2'))) { - return HubSpot::createWithOAuth2Token( - env('HUBSPOT_API_KEY', config('hubspot.api_key')), - null, - config('hubspot.client_options', []) + $this->includeConfig(); + + $this->app->bind(Discovery::class, function ($app) { + $this->includeConfig(); + + $this->mergeConfigFrom( + __DIR__.'/config/hubspot.php', + 'hubspot' + ); + + $handlerStack = \GuzzleHttp\HandlerStack::create(); + + if (config('hubspot.enable_constant_delay')) { + $handlerStack->push( + \HubSpot\RetryMiddlewareFactory::createRateLimitMiddleware( + \HubSpot\Delay::getConstantDelayFunction() + ) ); } - return HubSpot::create( - env('HUBSPOT_API_KEY', config('hubspot.api_key')), - null, - config('hubspot.client_options', []) + if ($exponentialDelay = config('hubspot.exponential_delay')) { + $handlerStack->push( + \HubSpot\RetryMiddlewareFactory::createRateLimitMiddleware( + \HubSpot\Delay::getExponentialDelayFunction($exponentialDelay) + ) + ); + } + + $client = new Client(array_merge( + config('hubspot.client_options'), + [ + 'handler' => $handlerStack, + ] + )); + + if ($accessToken = config('hubspot.access_token')) { + return Factory::createWithAccessToken( + $accessToken, + $client, + ); + } + + return Factory::createWithDeveloperApiKey( + config('hubspot.developer_key'), + $client, ); }); } @@ -39,4 +70,12 @@ public function boot() __DIR__.'/config/hubspot.php' => config_path('hubspot.php'), ], 'config'); } + + protected function includeConfig(): void + { + $this->mergeConfigFrom( + __DIR__.'/config/hubspot.php', + 'hubspot' + ); + } } diff --git a/src/config/hubspot.php b/src/config/hubspot.php index 51f69bd..d5a2d05 100644 --- a/src/config/hubspot.php +++ b/src/config/hubspot.php @@ -2,16 +2,28 @@ return [ /* - * Either the API key or the private app access token + * Connect to the Hubspot API using a private app access token */ - 'api_key' => env('HUBSPOT_API_KEY'), + 'access_token' => env('HUBSPOT_ACCESS_TOKEN'), /* - * Should the library connect via OAuth2 or use the API key. The usage of the API key will be deprecated on - * November 30th, 2022. + * Connect to the Hubspot API using a Developer API Key */ - 'use_oauth2' => env('HUBSPOT_USE_OAUTH2', false), + 'developer_key' => env('HUBSPOT_DEVELOPER_KEY'), + /* + * Options to enable built in middlewares to handle rate limiting + * + * @see https://github.com/HubSpot/hubspot-api-php#api-client-comes-with-middleware-for-implementation-of-rate-and-concurrent-limiting + */ + 'enable_constant_delay' => false, + 'exponential_delay' => null, + + /* + * Guzzle Client options that are user for Hubspot API requests + * + * @see https://docs.guzzlephp.org/en/stable/request-options.html + */ 'client_options' => [ 'http_errors' => true, ], diff --git a/tests/API/ContactsTest.php b/tests/API/ContactsTest.php index 4d8a574..aa01beb 100644 --- a/tests/API/ContactsTest.php +++ b/tests/API/ContactsTest.php @@ -2,22 +2,24 @@ namespace Tests\API; -use Rossjcooper\LaravelHubSpot\HubSpot; +use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations; +use HubSpot\Discovery\Discovery; use Tests\TestCase; class ContactsTest extends TestCase { public function test_get_contacts() { - $hubspot = app(HubSpot::class); + /** @var Discovery $hubspot */ + $hubspot = app(Discovery::class); - $response = $hubspot->contacts()->all(); + $response = $hubspot->crm()->contacts()->basicApi()->getPage(); - $contact = $response->contacts[0]; + $this->assertIsArray($response->getResults()); - $this->assertIsArray($response->contacts); // Test we have the default test contact - $this->assertEquals('Maria', $contact->properties->firstname->value); - $this->assertEquals('Johnson (Sample Contact)', $contact->properties->lastname->value); + /** @var SimplePublicObjectWithAssociations $contact */ + $contact = $response->getResults()[0]; + $this->assertEquals('Maria', $contact->getProperties()['firstname']); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 7df5fe9..23ab3a8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ namespace Tests; use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables; +use Orchestra\Testbench\Bootstrap\LoadConfiguration; use Orchestra\Testbench\TestCase as OrchTestCase; use Rossjcooper\LaravelHubSpot\HubSpotServiceProvider; @@ -23,6 +24,7 @@ protected function getEnvironmentSetUp($app) { $app->useEnvironmentPath(__DIR__.'/..'); $app->bootstrapWith([LoadEnvironmentVariables::class]); + $app->bootstrapWith([LoadConfiguration::class]); parent::getEnvironmentSetUp($app); } } diff --git a/tests/Unit/Facades/HubspotFacadeTest.php b/tests/Unit/Facades/HubspotFacadeTest.php new file mode 100644 index 0000000..6a5b88f --- /dev/null +++ b/tests/Unit/Facades/HubspotFacadeTest.php @@ -0,0 +1,16 @@ +assertInstanceOf(Discovery::class, HubSpot::getFacadeRoot()); + } +} diff --git a/tests/Unit/ServiceProviderTest.php b/tests/Unit/ServiceProviderTest.php index 8548d2d..7c353fa 100644 --- a/tests/Unit/ServiceProviderTest.php +++ b/tests/Unit/ServiceProviderTest.php @@ -2,34 +2,57 @@ namespace Tests\Unit; +use HubSpot\Discovery\Discovery; use Illuminate\Support\Facades\Config; -use Rossjcooper\LaravelHubSpot\HubSpot; use Tests\TestCase; class ServiceProviderTest extends TestCase { public function test_service_provider_bindings() { - $hubspot = app(HubSpot::class); + $hubspot = app(Discovery::class); - $this->assertInstanceOf(HubSpot::class, $hubspot); + $this->assertInstanceOf(Discovery::class, $hubspot); } - public function test_api_key_set() + /** @test */ + public function private_app_access_token_is_set_correctly() { - $hubspot = app(HubSpot::class); + $apiKey = 'MySecretKey'; - $this->assertEquals(env('HUBSPOT_API_KEY'), $hubspot->getClient()->key); + Config::set('hubspot.access_token', $apiKey); + + /** @var Discovery $hubspot */ + $hubspot = app(Discovery::class); + + /* + * Use reflection API to access private/protected properties + */ + $config = (new \ReflectionObject($hubspot))->getParentClass()->getProperty('config'); + $config->setAccessible(true); + + $accessToken = (new \ReflectionObject($config->getValue($hubspot)))->getProperty('accessToken'); + $accessToken->setAccessible(true); + + $this->assertEquals($apiKey, $accessToken->getValue($config->getValue($hubspot))); } - public function test_oauth2_client_is_built() + /** @test */ + public function developer_key_is_set_correctly() { - Config::set('hubspot.use_oauth2', true); - Config::set('hubspot.api_key', 'FooBarBaz'); + $devKey = 'MySecretKey'; + + Config::set('hubspot.access_token', null); + Config::set('hubspot.developer_key', $devKey); + + $hubspot = app(Discovery::class); + + $config = (new \ReflectionObject($hubspot))->getParentClass()->getProperty('config'); + $config->setAccessible(true); - $hubspot = app(HubSpot::class); + $accessToken = (new \ReflectionObject($config->getValue($hubspot)))->getProperty('developerApiKey'); + $accessToken->setAccessible(true); - $this->assertEquals(env('HUBSPOT_API_KEY'), $hubspot->getClient()->key); - $this->assertTrue($hubspot->getClient()->oauth2); + $this->assertEquals($devKey, $accessToken->getValue($config->getValue($hubspot))); } }