-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Harm Smits
committed
Oct 20, 2020
0 parents
commit dc8219f
Showing
18 changed files
with
1,237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/vendor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "best-brands/kiyoh-api", | ||
"type": "library", | ||
"description": "A PHP Client for the Kiyoh API with async support and full object mapping", | ||
"keywords": [ | ||
"api", | ||
"php", | ||
"kiyoh" | ||
], | ||
"homepage": "http://github.com/best-brands/kiyoh-client", | ||
"license": "MIT", | ||
"authors": [ | ||
{ | ||
"name": "Harm Smits", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"require": { | ||
"php": ">=7.4", | ||
"ext-json": "*", | ||
"guzzlehttp/guzzle": "^7.2.0" | ||
}, | ||
"require-dev": { | ||
"nette/php-generator": "^3.3", | ||
"phpunit/phpunit": "^9.1" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"BestBrands\\KiyohClient\\": "src/" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
use BestBrands\KiyohClient\Client; | ||
use BestBrands\KiyohClient\Exceptions\KiyohException; | ||
use BestBrands\KiyohClient\Models\Invitee; | ||
|
||
require "vendor/autoload.php"; | ||
|
||
$client = new Client(...); | ||
$location_id = ...; | ||
/** | ||
* Send an invite to a client | ||
*/ | ||
try { | ||
if ($client->invite((new Invitee()) | ||
->setInviteEmail('[email protected]') | ||
->setDelay(1) | ||
->setFirstName('Harm') | ||
->setLastName('Smits') | ||
->setLocationId($location_id) | ||
->setRefCode(...) | ||
)->getCode() === 'A') { | ||
echo 'Invite is scheduled'; | ||
} else { | ||
echo 'Invite is not scheduled'; | ||
} | ||
} catch (KiyohException $exception) { | ||
echo 'Invite is not scheduled'; | ||
} | ||
|
||
/** | ||
* Iterate over the reviews | ||
*/ | ||
($date = new DateTime())->setDate(2020, 01, 01); | ||
foreach ($client->getReviews($location_id, $date)->getReviews() as $review) | ||
echo "Review written by: " . $review->getReviewAuthor() . "\n"; | ||
|
||
/** | ||
* Get location statistics | ||
*/ | ||
$stats = $client->getLocationStatistics($location_id); | ||
echo "Average rating of last 12 months: " . $stats->getLast12MonthAverageRating() . "\n"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
<?php | ||
|
||
namespace BestBrands\KiyohClient; | ||
|
||
use BestBrands\KiyohClient\Exceptions\KiyohException; | ||
use BestBrands\KiyohClient\Exceptions\Notice; | ||
use BestBrands\KiyohClient\Models\Invitee; | ||
use BestBrands\KiyohClient\Models\Location; | ||
use BestBrands\KiyohClient\Models\LocationStatistics; | ||
use BestBrands\KiyohClient\Models\ReducedLocation; | ||
use BestBrands\KiyohClient\Models\Response; | ||
use Closure; | ||
use DateTime; | ||
use GuzzleHttp\Exception\GuzzleException; | ||
use GuzzleHttp\Exception\RequestException; | ||
use GuzzleHttp\HandlerStack; | ||
use GuzzleHttp\Middleware; | ||
use GuzzleHttp\Promise\Promise; | ||
use GuzzleHttp\Promise\PromiseInterface; | ||
use GuzzleHttp\Promise\Utils; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\StreamInterface; | ||
use Throwable; | ||
|
||
/** | ||
* Class Client | ||
* @package BestBrands\KiyohClient | ||
* | ||
* @method ReducedLocation[] getLatestStatistics(DateTime $since, ?DateTime $updated_since = null, int $limit = 50) | ||
* @method PromiseInterface getLatestStatisticsAsync(DateTime $since, ?DateTime $updated_since = null, int $limit = 50) | ||
* @method Location getReviews(string $locationId, DateTime $since, array $params = []) | ||
* @method PromiseInterface getReviewsAsync(string $locationId, DateTime $since, array $params = []) | ||
* @method string getLastRemovedReview(DateTime $updatedSince) | ||
* @method PromiseInterface getLastRemovedReviewAsync(DateTime $updatedSince) | ||
* @method LocationStatistics getLocationStatistics(string $locationId) | ||
* @method PromiseInterface getLocationStatisticsAsync(string $locationId) | ||
* @method Response invite(Invitee $invitee) | ||
* @method PromiseInterface inviteAsync(Invitee $invitee) | ||
*/ | ||
class Client | ||
{ | ||
/** @var string base url */ | ||
const API_URL_KIYOH_COM = 'https://www.kiyoh.com/'; | ||
|
||
/** @var string base url */ | ||
const API_URL_KLANTEN_VERTELLEN = 'https://www.klantenvertellen.nl'; | ||
|
||
/** @var string[] all the base urls that are allowed */ | ||
private const BASE_URLS = [ | ||
self::API_URL_KIYOH_COM, | ||
self::API_URL_KLANTEN_VERTELLEN, | ||
]; | ||
|
||
/** @var \GuzzleHttp\Client */ | ||
protected \GuzzleHttp\Client $client; | ||
|
||
/** @var Request request dispatcher */ | ||
protected Request $request; | ||
|
||
/** @var Populator */ | ||
protected Populator $populator; | ||
|
||
/** @var string holds the current API client id */ | ||
protected string $token; | ||
|
||
/** @var string holds the base url for the api requests */ | ||
protected string $baseUrl; | ||
|
||
/** | ||
* Client constructor. | ||
* | ||
* @param string $token | ||
* @param string $base_url | ||
* @throws Notice | ||
*/ | ||
public function __construct(string $token, string $base_url = self::API_URL_KIYOH_COM) | ||
{ | ||
$stack = HandlerStack::create(); | ||
$stack->push(Middleware::mapRequest($this->getRequestHandler())); | ||
$stack->push(Middleware::retry($this->getRetryHandler())); | ||
|
||
if (!in_array(($this->baseUrl = $base_url), self::BASE_URLS)) | ||
throw new Notice('Unrecognized base url'); | ||
|
||
$this->token = $token; | ||
$this->client = new \GuzzleHttp\Client([ | ||
'handler' => $stack, | ||
'base_url' => $this->baseUrl, | ||
'timeout' => 1 | ||
]); | ||
$this->request = new Request(); | ||
$this->populator = new Populator(); | ||
} | ||
|
||
/** | ||
* Unwrap an array of async requests, very useful when stacking multiple | ||
* | ||
* @param array $promises | ||
* | ||
* @return array | ||
* @throws Throwable | ||
*/ | ||
public function unwrap(array $promises) | ||
{ | ||
return Utils::unwrap($promises); | ||
} | ||
|
||
/** | ||
* Get the retry handler | ||
* | ||
* @return Closure | ||
*/ | ||
private function getRetryHandler() | ||
{ | ||
return function ($retries, ?RequestInterface $request, ?ResponseInterface $response, ?RequestException $exception) { | ||
if ($response && $response->getStatusCode() !== 200) | ||
throw new KiyohException(json_decode($response->getBody(), true)); | ||
|
||
return false; | ||
}; | ||
} | ||
|
||
/** | ||
* Get the request handler | ||
* | ||
* @return Closure | ||
*/ | ||
private function getRequestHandler() | ||
{ | ||
return function (RequestInterface $request) { | ||
return $request->withHeader('X-Publication-Api-Token', $this->token); | ||
}; | ||
} | ||
|
||
/** | ||
* Dispatch the request. This is some serious sorcery not to be touched. | ||
* | ||
* @param string $method | ||
* @param array $args | ||
* | ||
* @return array|string|Promise | ||
* @throws GuzzleException|KiyohException | ||
*/ | ||
public function __call(string $method, array $args) | ||
{ | ||
if ($async = (substr($method, -5) === 'Async')) | ||
$method = substr($method, 0, -5); | ||
|
||
[$method, $url, $data, $response] = call_user_func_array([$this->request, $method], $args); | ||
|
||
return ($async) | ||
? $this->handleAsyncRequest($method, $url, $data, $response) | ||
: $this->handleRequest($method, $url, $data, $response); | ||
} | ||
|
||
/** | ||
* Handle a non-blocking request | ||
* | ||
* @param $method | ||
* @param $url | ||
* @param $data | ||
* @param $responseFormat | ||
* | ||
* @return PromiseInterface | ||
*/ | ||
private function handleAsyncRequest($method, $url, $data, array $responseFormat): PromiseInterface | ||
{ | ||
return $this->client->requestAsync($method, $url, $data) | ||
->then(function (ResponseInterface $response) use (&$responseFormat) { | ||
return $this->handleResponse($response, $responseFormat); | ||
}); | ||
} | ||
|
||
/** | ||
* Handle a blocking request | ||
* | ||
* @param string $method | ||
* @param string $url | ||
* @param array $data | ||
* @param array $responseFormat | ||
* | ||
* @return array|mixed|StreamInterface | ||
* @throws GuzzleException|KiyohException | ||
*/ | ||
private function handleRequest(string $method, string $url, array $data, array $responseFormat) | ||
{ | ||
try { | ||
$result = $this->client->request($method, $url, $data); | ||
} catch (RequestException $exception) { | ||
return $this->handleResponse($exception->getResponse(), $responseFormat); | ||
} | ||
|
||
return $this->handleResponse($result, $responseFormat); | ||
} | ||
|
||
/** | ||
* Handle the response accordingly | ||
* | ||
* @param ResponseInterface $response | ||
* @param array $responseFormat | ||
* | ||
* @return array|mixed|StreamInterface | ||
* @throws KiyohException | ||
*/ | ||
private function handleResponse(ResponseInterface $response, array $responseFormat) | ||
{ | ||
if ($response->getStatusCode() !== 200) { | ||
throw new KiyohException(json_decode($response->getBody(), true)); | ||
} else if ($responseFormat && isset($responseFormat[$response->getStatusCode()])) { | ||
return $this->populator->populate( | ||
$responseFormat[$response->getStatusCode()], | ||
json_decode($response->getBody(), true) | ||
); | ||
} | ||
|
||
return $response->getBody(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
namespace BestBrands\KiyohClient\Exceptions; | ||
|
||
class InvalidArgumentException extends \InvalidArgumentException | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
namespace BestBrands\KiyohClient\Exceptions; | ||
|
||
use Exception; | ||
use Throwable; | ||
|
||
/** | ||
* Class KiyohException | ||
* @package BestBrands\KiyohClient\Exceptions | ||
*/ | ||
class KiyohException extends Exception | ||
{ | ||
protected array $rawError; | ||
|
||
/** | ||
* KiyohException constructor. | ||
* | ||
* @param array $message | ||
* @param int $code | ||
* | ||
* | ||
* @param Throwable|null $previous | ||
*/ | ||
public function __construct(array $message, $code = 0, Throwable $previous = null) | ||
{ | ||
$this->rawError = $message; | ||
parent::__construct($this->getIndentMessage($message), $code, $previous); | ||
} | ||
|
||
/** | ||
* Creates a formatted message from an array | ||
* | ||
* @param array $message | ||
* @param int $indent | ||
* | ||
* @return string | ||
*/ | ||
public function getIndentMessage(array $message, int $indent = 0) | ||
{ | ||
$formatted = ''; | ||
|
||
foreach ($message as $key => $value) { | ||
if (is_array($value)) { | ||
$formatted .= $this->getIndentMessage($value, $indent + 4); | ||
} else { | ||
$formatted .= sprintf("%s%s: %s\n", str_repeat(" ", $indent), $key, $value); | ||
} | ||
} | ||
|
||
return $formatted; | ||
} | ||
|
||
/** | ||
* Get the raw error | ||
* | ||
* @return array | ||
*/ | ||
public function getRawError(): array | ||
{ | ||
return $this->rawError; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
namespace BestBrands\KiyohClient\Exceptions; | ||
|
||
/** | ||
* Class Notice | ||
* @package BestBrands\KiyohClient\Exceptions | ||
*/ | ||
class Notice extends \Exception | ||
{ | ||
} |
Oops, something went wrong.