diff --git a/src/Calendly.php b/src/Calendly.php index 57af5a9..9d21a25 100644 --- a/src/Calendly.php +++ b/src/Calendly.php @@ -9,6 +9,7 @@ * @method static Models\User getUser(string $uuid) * @method static Models\Organization getOrganization() * @method static Models\EventType[] getEventTypes(array $options) + * @method static array request(string $uri, string $method = "GET", array $params = []) */ class Calendly { diff --git a/src/CalendlyApi.php b/src/CalendlyApi.php index 23a957c..de52594 100644 --- a/src/CalendlyApi.php +++ b/src/CalendlyApi.php @@ -8,6 +8,9 @@ use LoBrs\Calendly\Exceptions\ApiErrorException; use LoBrs\Calendly\Exceptions\InternalServerErrorException; use LoBrs\Calendly\Exceptions\InvalidArgumentException; +use LoBrs\Calendly\Exceptions\PermissionDeniedException; +use LoBrs\Calendly\Exceptions\ResourceNotFoundException; +use LoBrs\Calendly\Exceptions\UnauthenticatedException; use LoBrs\Calendly\Models\EventType; use LoBrs\Calendly\Models\Organization; use LoBrs\Calendly\Models\User; @@ -15,18 +18,23 @@ final class CalendlyApi { const API_URL = "https://api.calendly.com"; + private Client $client; /** - * @var string Token utilisateur Calendly + * @var string Calendly `user` or `organization` token */ private string $token; - public function __construct(string $token) { + public function __construct(string $token, ?Client $httpClient = null) { $this->token = $token; - $this->client = new Client([ - "base_uri" => self::API_URL, - ]); + if ($httpClient) { + $this->client = $httpClient; + } else { + $this->client = new Client([ + "base_uri" => self::API_URL, + ]); + } } /** @@ -49,22 +57,42 @@ public function request(string $uri, string $method = "GET", array $params = []) } $data = json_decode($response->getBody(), true); - if ($response->getStatusCode() > 299) { + $httpCode = $response->getStatusCode(); + if ($httpCode > 299) { $message = $data['message'] ?? "Une erreur est survenue"; + $details = $data['details'] ?? []; if (isset($data['title'])) { + // get exception class from title $exceptionClassname = "LoBrs\\Calendly\\Exceptions\\" . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $data['title']))) . "Exception"; if (class_exists($exceptionClassname)) { - throw new $exceptionClassname($message, $data['details'] ?? []); + throw new $exceptionClassname($message, $details); } } - throw new ApiErrorException($message, $data['details'] ?? []); + if ($httpCode == 500) { + throw new InternalServerErrorException($message, $details); + } + if ($httpCode == 404) { + throw new ResourceNotFoundException($message, $details); + } + if ($httpCode == 403) { + throw new PermissionDeniedException($message, $details); + } + if ($httpCode == 401) { + throw new UnauthenticatedException($message, $details); + } + + // Default API error exception + throw new ApiErrorException($message, $details); } return $data; } + /** + * @throws ApiErrorException|InvalidArgumentException + */ public function me(): ?User { $response = $this->request("/users/me"); if (isset($response["resource"])) { @@ -73,6 +101,9 @@ public function me(): ?User { return null; } + /** + * @throws ApiErrorException|InvalidArgumentException + */ public function getOrganization(?string $organization_uuid = null): ?Organization { if (empty($organization_uuid)) { return $this->me()->getCurrentOrganization(); @@ -81,6 +112,9 @@ public function getOrganization(?string $organization_uuid = null): ?Organizatio return Organization::get($organization_uuid); } + /** + * @throws ApiErrorException|InvalidArgumentException + */ public function getUser(?string $uuid = null): ?User { if (empty($uuid)) { return $this->me(); @@ -89,6 +123,9 @@ public function getUser(?string $uuid = null): ?User { return User::get($uuid); } + /** + * @throws ApiErrorException|InvalidArgumentException + */ public function getEventTypes(array $options): Utils\PaginatedList { return EventType::paginate($options); } diff --git a/tests/CalendlyApiTest.php b/tests/CalendlyApiTest.php new file mode 100644 index 0000000..71616c2 --- /dev/null +++ b/tests/CalendlyApiTest.php @@ -0,0 +1,52 @@ +mockHandler = new MockHandler(); + $httpClient = new Client([ + 'handler' => HandlerStack::create($this->mockHandler), + ]); + + $this->api = new CalendlyApi("secret", $httpClient); + } + + public function testOkResponse() { + + $this->mockHandler->append(new Response(200, [ + 'content-type' => 'application/json; charset=utf-8', + ], '{"foo": "bar"}')); + + $response = $this->api->request("/foo"); + $this->assertEquals("bar", $response["foo"]); + } + + public function testExceptionResponse() { + + $this->expectException(InvalidArgumentException::class); + + $this->mockHandler->append(new Response(400, [ + 'content-type' => 'application/json; charset=utf-8', + ], '{"title": "Invalid Argument", "message": "There is an error", "details": [{"foo":"bar"}]}')); + + $this->api->request("/foo"); + $this->assertIsArray($this->getExpectedException()->getDetails()); + $this->assertEquals("There is an error", $this->getExpectedException()->getMessage()); + } + +} \ No newline at end of file