From 10c5c834b01ed2d1bbc2fa703f621f12a022ef1c Mon Sep 17 00:00:00 2001 From: Eskrano Date: Tue, 10 Jan 2023 00:32:17 +0200 Subject: [PATCH] Update for error code 429. --- .idea/php.xml | 4 +- .idea/quickbase-rest-api.loc.iml | 2 + composer.json | 2 +- composer.lock | 360 ++++++++++++++++++++++++---- src/QuickbaseRest/QuickbaseREST.php | 102 +++++--- 5 files changed, 383 insertions(+), 87 deletions(-) diff --git a/.idea/php.xml b/.idea/php.xml index 0418565..904de3a 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -9,7 +9,9 @@ + + - + \ No newline at end of file diff --git a/.idea/quickbase-rest-api.loc.iml b/.idea/quickbase-rest-api.loc.iml index 0a0d30f..ae2ee0c 100644 --- a/.idea/quickbase-rest-api.loc.iml +++ b/.idea/quickbase-rest-api.loc.iml @@ -9,6 +9,8 @@ + + diff --git a/composer.json b/composer.json index 8264a39..7f158b0 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,6 @@ } }, "require": { - "guzzlehttp/guzzle": "^6.0" + "guzzlehttp/guzzle": ">=7.0" } } diff --git a/composer.lock b/composer.lock index cffc597..c8567a8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,38 +4,39 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "045658d81f6d9d3243e731dda7bf04d1", + "content-hash": "653c299bda1046a4ad711f544dc61d47", "packages": [ { "name": "guzzlehttp/guzzle", - "version": "7.0.1", + "version": "7.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "2d9d3c186a6637a43193e66b097c50e4451eaab2" + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/2d9d3c186a6637a43193e66b097c50e4451eaab2", - "reference": "2d9d3c186a6637a43193e66b097c50e4451eaab2", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.6.1", - "php": "^7.2.5", - "psr/http-client": "^1.0" + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.9 || ^2.4", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" }, "require-dev": { - "ergebnis/composer-normalize": "^2.0", + "bamarni/composer-bin-plugin": "^1.8.1", "ext-curl": "*", - "php-http/client-integration-tests": "dev-phpunit8", - "phpunit/phpunit": "^8.5.5", - "psr/log": "^1.1" + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { "ext-curl": "Required for CURL handler support", @@ -44,36 +45,64 @@ }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "7.5-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", @@ -85,115 +114,196 @@ "rest", "web service" ], - "time": "2020-06-27T10:33:25+00:00" + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-08-28T15:39:27+00:00" }, { "name": "guzzlehttp/promises", - "version": "v1.3.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + "reference": "b94b2807d85443f9719887892882d0329d1e2598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", + "reference": "b94b2807d85443f9719887892882d0329d1e2598", "shasum": "" }, "require": { - "php": ">=5.5.0" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^4.0" + "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2022-08-28T14:55:35+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.6.1", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + "reference": "67c26b443f348a51926030c83481b85718457d3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", - "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", + "reference": "67c26b443f348a51926030c83481b85718457d3d", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "suggest": { - "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "2.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", @@ -207,7 +317,25 @@ "uri", "url" ], - "time": "2019-07-01T23:21:34+00:00" + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.4.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-10-26T14:07:24+00:00" }, { "name": "psr/http-client", @@ -256,8 +384,66 @@ "psr", "psr-18" ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, "time": "2020-06-29T06:28:15+00:00" }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -306,6 +492,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, "time": "2016-08-06T14:39:51+00:00" }, { @@ -346,7 +535,78 @@ } ], "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" } ], "packages-dev": [], @@ -357,5 +617,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.3.0" } diff --git a/src/QuickbaseRest/QuickbaseREST.php b/src/QuickbaseRest/QuickbaseREST.php index 0d30acf..b19360e 100644 --- a/src/QuickbaseRest/QuickbaseREST.php +++ b/src/QuickbaseRest/QuickbaseREST.php @@ -20,24 +20,25 @@ class QuickbaseREST { + /** + * @var bool + */ + public $convert_json_to_array = true; /** * cURL resource * @var */ protected $guzzle; - /** * @var string */ protected $app_token; - /** * Permament User token * * @var string */ protected $user_token; - /** * Your quickbase app subdomain * @@ -46,22 +47,24 @@ class QuickbaseREST * @var string */ protected $realm; - /** * Loaded components * @var array */ protected $loaded_sections = []; - /** - * @var bool + * @var PerformanceMonitor */ - public $convert_json_to_array = true; + protected $performance; /** - * @var PerformanceMonitor + * @var array|bool[] */ - protected $performance; + protected array $default_config = [ + 'wait_on_exceeded' => true, + ]; + + protected array $config = []; /** * QuickbaseREST constructor. @@ -70,7 +73,8 @@ class QuickbaseREST public function __construct( string $realm, string $user_token, - bool $preload_sections = true + bool $preload_sections = true, + array $config = [], ) { $this->realm = $realm; @@ -96,6 +100,12 @@ public function __construct( } $this->performance = new PerformanceMonitor(); + + /** + * Load config + */ + + $this->config = array_merge($this->default_config, $config); } /** @@ -140,14 +150,6 @@ public function tables(): Tables return $this->getSection(Tables::class); } - /** - * @return PerformanceMonitor - */ - public function getPerformanceMonitor(): PerformanceMonitor - { - return $this->performance; - } - /** * @param string $method * @param string $action @@ -159,29 +161,59 @@ public function getPerformanceMonitor(): PerformanceMonitor public function query(string $method, string $action, array $body, array $query_params = []): ResponseInterface { try { - $_start = microtime(true); + $response = $this->runQuery($method, $action, $body, $query_params); - $response = $this->guzzle->request( - $method, - sprintf("%s?%s", $action, http_build_query($query_params)), - [ - RequestOptions::BODY => json_encode($body), - ] - ); + return $response; + } catch (RequestException $exception) { - $total_ms = microtime(true) - $_start; + if ($this->config['wait_on_exceeded'] && $exception->getResponse()->getStatusCode() === 429) { - $this->getPerformanceMonitor() - ->save([ - 'query' => compact('method', 'action', 'body', 'query_params'), - 'ms' => $total_ms - ]); + $header_key = 'x-ratelimit-reset'; - return $response; + if ($exception->getResponse()->hasHeader($header_key)) { + $sleep_time = $exception->getResponse()->getHeader($header_key)[0] ?? null; + + + if (null !== $sleep_time && intval($sleep_time) > 0) { + sleep(intval($sleep_time)); + + return $this->runQuery($method, $action, $body, $query_params); + } + } + } - } catch (RequestException $exception) { - throw $exception; return $exception->getResponse(); } } + + protected function runQuery(string $method, string $action, array $body, array $query_params = []): ResponseInterface + { + $_start = microtime(true); + + $response = $this->guzzle->request( + $method, + sprintf("%s?%s", $action, http_build_query($query_params)), + [ + RequestOptions::BODY => json_encode($body), + ] + ); + + $total_ms = microtime(true) - $_start; + + $this->getPerformanceMonitor() + ->save([ + 'query' => compact('method', 'action', 'body', 'query_params'), + 'ms' => $total_ms + ]); + + return $response; + } + + /** + * @return PerformanceMonitor + */ + public function getPerformanceMonitor(): PerformanceMonitor + { + return $this->performance; + } } \ No newline at end of file