diff --git a/src/Controller/Component/ApiComponent.php b/src/Controller/Component/ApiComponent.php new file mode 100644 index 0000000..4ef0755 --- /dev/null +++ b/src/Controller/Component/ApiComponent.php @@ -0,0 +1,148 @@ + for more details. + */ +namespace BEdita\WebTools\Controller\Component; + +use BEdita\SDK\BEditaClient; +use Cake\Controller\Component; + +/** + * Proxy component for BEdita API. + * + * @method void setupTokens(array $tokens) + * @method array getDefaultHeaders() + * @method string getApiBaseUrl() + * @method array getTokens() + * @method \Psr\Http\Message\ResponseInterface|null getResponse() + * @method int|null getStatusCode() + * @method string|null getStatusMessage() + * @method array|null getResponseBody() + * @method array|null authenticate(string $username, string $password) + * @method array|null get(string $path, ?array $query = null, ?array $headers = null) + * @method array|null getObjects(string $type = 'objects', ?array $query = null, ?array $headers = null) + * @method array|null getObject($id, string $type = 'objects', ?array $query = null, ?array $headers = null) + * @method array|null getRelated($id, string $type, string $relation, ?array $query = null, ?array $headers = null) + * @method array|null addRelated($id, string $type, string $relation, array $data, ?array $headers = null) + * @method array|null removeRelated($id, string $type, string $relation, array $data, ?array $headers = null) + * @method array|null replaceRelated($id, string $type, string $relation, array $data, ?array $headers = null) + * @method array|null save(string $type, array $data, ?array $headers = null) + * @method array|null deleteObject($id, string $type) + * @method array|null remove($id) + * @method array|null upload(string $filename, string $filepath, ?array $headers = null) + * @method array|null createMediaFromStream($streamId, string $type, array $body) + * @method array|null thumbs($id = null, $query = []) + * @method array|null schema(string $type) + * @method array|null relationData(string $name) + * @method array|null restoreObject($id, string $type) + * @method array|null patch(string $path, $body, ?array $headers = null) + * @method array|null post(string $path, $body, ?array $headers = null) + * @method array|null delete(string $path, $body = null, ?array $headers = null) + * @method void refreshTokens() + */ +class ApiComponent extends Component +{ + /** + * Default component configuration. + * + * - apiClient => the BEditaClient instance + * + * @var array + */ + protected $_defaultConfig = [ + 'apiClient' => null, + 'exceptionsEnabled' => true, + ]; + + /** + * Keep last response error + * + * @var \Throwable + */ + protected $error = null; + + /** + * Return the instance of BEditaClient + * + * @return \BEdita\SDK\BEditaClient + */ + public function getClient(): BEditaClient + { + $client = $this->getConfigOrFail('apiClient'); + if (!$client instanceof BEditaClient) { + throw new \InvalidArgumentException(__('Not a valid api client class')); + } + + return $client; + } + + /** + * Set if the client exceptions will be thrown. + * + * @param bool $value The value to set + * @return $this + */ + public function enableExceptions(bool $value) + { + $this->setConfig('exceptionsEnabled', $value); + + return $this; + } + + /** + * Say if there was error in the last API request. + * Note that error is set if `exceptionsEnabled` conf is `false` + * + * @return array + */ + public function hasError(): bool + { + return $this->error !== null; + } + + /** + * Get the last API error. + * Note that error is set if `exceptionsEnabled` conf is `false` + * + * @return \Throwable|null + */ + public function getError(): ?\Throwable + { + return $this->error; + } + + /** + * Proxy to BEditaClient methods. + * + * @param string $name The method invoked + * @param array $arguments The arguments for the method + * @return mixed + */ + public function __call($name, array $arguments) + { + $response = null; + $this->error = null; + try { + $response = call_user_func_array([$this->getClient(), $name], $arguments); + } catch (\Throwable $e) { + if ($this->getConfig('exceptionsEnabled') === true) { + throw $e; + } + $this->log($e->getMessage(), 'error'); + + $this->error = $e; + } + + return $response; + } +} diff --git a/tests/TestCase/Controller/Component/ApiComponentTest.php b/tests/TestCase/Controller/Component/ApiComponentTest.php new file mode 100644 index 0000000..25fb7de --- /dev/null +++ b/tests/TestCase/Controller/Component/ApiComponentTest.php @@ -0,0 +1,148 @@ + for more details. + */ +namespace BEdita\WebTools\Test\TestCase\Controller\Component; + +use BEdita\SDK\BEditaClient; +use BEdita\SDK\BEditaClientException; +use BEdita\WebTools\ApiClientProvider; +use BEdita\WebTools\Controller\Component\ApiComponent; +use Cake\Controller\ComponentRegistry; +use Cake\TestSuite\TestCase; +use Cake\Utility\Hash; + +/** + * {@see \BEdita\WebTools\Controller\Component\ApiComponent} Test Case + * + * @coversDefaultClass \BEdita\WebTools\Controller\Component\ApiComponent + */ +class ApiComponentTest extends TestCase +{ + /** + * The ApiComponent instance + * + * @var \BEdita\WebTools\Controller\Component\ApiComponent + */ + protected $ApiComponent = null; + + public function setUp(): void + { + parent::setUp(); + + $this->ApiComponent = new ApiComponent(new ComponentRegistry()); + $this->ApiComponent->setConfig('apiClient', ApiClientProvider::getApiClient()); + } + + /** + * Data provider for testGetClient + * + * @return array + */ + public function getClientProvider(): array + { + return [ + 'missingClient' => [ + new \InvalidArgumentException(), + null, + ], + 'wrongClient' => [ + new \InvalidArgumentException(), + 'GustavoClient', + ], + 'ok' => [ + BEditaClient::class, + ApiClientProvider::getApiClient(), + ], + ]; + } + + /** + * Test for getClient() + * + * @param mixed $expected The expected value + * @param mixed $apiClient The api client for configuration + * @return void + * + * @dataProvider getClientProvider + * @covers ::getClient() + */ + public function testGetClient($expected, $apiClient): void + { + if ($expected instanceof \Exception) { + $this->expectException(\InvalidArgumentException::class); + } + + $this->ApiComponent->setConfig('apiClient', $apiClient); + $client = $this->ApiComponent->getClient(); + static::assertInstanceOf($expected, $client); + } + + /** + * Test for enableExceptions + * + * @return void + * + * @covers ::enableExceptions() + */ + public function testEnableExceptions(): void + { + $this->ApiComponent->enableExceptions(false); + static::assertFalse($this->ApiComponent->getConfig('exceptionsEnabled')); + + $this->ApiComponent->enableExceptions(true); + static::assertTrue($this->ApiComponent->getConfig('exceptionsEnabled')); + } + + /** + * Test method proxied to BEditaClient + * + * @return void + * + * @covers ::__call() + */ + public function testProxyToApiClientOk(): void + { + $response = $this->ApiComponent->getObject(1); + static::assertEquals('1', Hash::get($response, 'data.id')); + } + + /** + * Test that BEditaClientException was thrown. + * + * @return void + * + * @covers ::__call() + */ + public function testExceptionThrown(): void + { + $this->expectException(BEditaClientException::class); + $this->ApiComponent->getObject(1000); + } + + /** + * Test that BEditaClientException was not thrown and error is populated. + * + * @return void + * + * @covers ::__call() + * @covers ::hasError() + * @covers ::getError() + */ + public function testError(): void + { + $this->ApiComponent->enableExceptions(false)->getObject(1000); + static::assertTrue($this->ApiComponent->hasError()); + static::assertInstanceOf(BEditaClientException::class, $this->ApiComponent->getError()); + } +}