From ab2ae5c5c8f6d090104ae9a9ed9dd55b2cefbb73 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Wed, 3 Apr 2024 20:21:59 +0200 Subject: [PATCH 1/2] Update error message on invalid authorization header --- tests/Integration/ApiPlatform/ApiTestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/ApiPlatform/ApiTestCase.php b/tests/Integration/ApiPlatform/ApiTestCase.php index 1f611e7..c2d8ca5 100644 --- a/tests/Integration/ApiPlatform/ApiTestCase.php +++ b/tests/Integration/ApiPlatform/ApiTestCase.php @@ -77,7 +77,7 @@ public function testProtectedEndpoints(string $method, string $uri, string $cont $content = $response->getContent(false); $this->assertNotEmpty($content); - $this->assertEquals('No Authorization header provided', $content); + $this->assertEquals('"No Authorization header provided"', $content); // Test same endpoint with a token but without scopes $emptyBearerToken = $this->getBearerToken(); From f379b4c7932779de213ec6ee701be21e2d151dcb Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Mon, 8 Apr 2024 19:17:46 +0200 Subject: [PATCH 2/2] Add API clients listing --- .../Resources/{ => ApiClient}/ApiClient.php | 54 +++----------- .../Resources/ApiClient/ApiClientList.php | 72 +++++++++++++++++++ .../ApiPlatform/ApiClientEndpointTest.php | 40 +++++++++++ 3 files changed, 123 insertions(+), 43 deletions(-) rename src/ApiPlatform/Resources/{ => ApiClient}/ApiClient.php (61%) create mode 100644 src/ApiPlatform/Resources/ApiClient/ApiClientList.php diff --git a/src/ApiPlatform/Resources/ApiClient.php b/src/ApiPlatform/Resources/ApiClient/ApiClient.php similarity index 61% rename from src/ApiPlatform/Resources/ApiClient.php rename to src/ApiPlatform/Resources/ApiClient/ApiClient.php index b90c6cc..572022d 100644 --- a/src/ApiPlatform/Resources/ApiClient.php +++ b/src/ApiPlatform/Resources/ApiClient/ApiClient.php @@ -20,7 +20,7 @@ declare(strict_types=1); -namespace PrestaShop\Module\APIResources\ApiPlatform\Resources; +namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\ApiClient; use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; @@ -39,52 +39,12 @@ new CQRSGet( uriTemplate: '/api-client/{apiClientId}', requirements: ['apiClientId' => '\d+'], - openapiContext: [ - 'summary' => 'Get API Client details', - 'description' => 'Get API Client public details only, sensitive information like secrets is not returned', - 'parameters' => [ - [ - 'name' => 'apiClientId', - 'in' => 'path', - 'required' => true, - 'schema' => [ - 'type' => 'string', - ], - 'description' => 'Id of the API Client you are requesting the details from', - ], - [ - 'name' => 'Authorization', - 'in' => 'scopes', - 'description' => 'api_client_read', - ], - ], - ], CQRSQuery: GetApiClientForEditing::class, scopes: ['api_client_read'] ), new CQRSDelete( uriTemplate: '/api-client/{apiClientId}', requirements: ['apiClientId' => '\d+'], - openapiContext: [ - 'summary' => 'Delete API Client details', - 'description' => 'Delete API Client public details only, sensitive information like secrets is not returned', - 'parameters' => [ - [ - 'name' => 'apiClientId', - 'in' => 'path', - 'required' => true, - 'schema' => [ - 'type' => 'string', - ], - 'description' => 'Id of the API Client you are deleting', - ], - [ - 'name' => 'Authorization', - 'in' => 'scopes', - 'description' => 'api_client_write', - ], - ], - ], output: false, CQRSQuery: DeleteApiClientCommand::class, scopes: ['api_client_write'] @@ -102,6 +62,7 @@ scopes: ['api_client_write'] ), ], + normalizationContext: ['skip_null_values' => false], exceptionToStatus: [ApiClientNotFoundException::class => 404], )] class ApiClient @@ -109,17 +70,24 @@ class ApiClient #[ApiProperty(identifier: true)] public int $apiClientId; - public string $secret; - public string $clientId; public string $clientName; public string $description; + public ?string $externalIssuer; + public bool $enabled; public int $lifetime; public array $scopes; + + /** + * Only used for the return of created API Client, it is the only endpoint where the secret is returned. + * + * @var string + */ + public string $secret; } diff --git a/src/ApiPlatform/Resources/ApiClient/ApiClientList.php b/src/ApiPlatform/Resources/ApiClient/ApiClientList.php new file mode 100644 index 0000000..2f274ab --- /dev/null +++ b/src/ApiPlatform/Resources/ApiClient/ApiClientList.php @@ -0,0 +1,72 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +declare(strict_types=1); + +namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\ApiClient; + +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use PrestaShop\PrestaShop\Core\Grid\Query\ApiClientQueryBuilder; +use PrestaShop\PrestaShop\Core\Search\Filters\ApiClientFilters; +use PrestaShopBundle\ApiPlatform\Metadata\DQBPaginatedList; + +#[ApiResource( + operations: [ + new DQBPaginatedList( + uriTemplate: '/api-clients', + scopes: [ + 'api_client_read', + ], + ApiResourceMapping: [ + '[id_api_client]' => '[apiClientId]', + '[client_id]' => '[clientId]', + '[client_name]' => '[clientName]', + '[external_issuer]' => '[externalIssuer]', + ], + queryBuilder: ApiClientQueryBuilder::class, + filtersClass: ApiClientFilters::class, + filtersMapping: [ + '[apiClientId]' => '[id_api_client]', + '[clientId]' => '[client_id]', + '[clientName]' => '[client_name]', + '[externalIssuer]' => '[external_issuer]', + ], + ), + ], + normalizationContext: ['skip_null_values' => false], +)] +class ApiClientList +{ + #[ApiProperty(identifier: true)] + public int $apiClientId; + + public string $clientId; + + public string $clientName; + + public string $description; + + public ?string $externalIssuer; + + public bool $enabled; + + public int $lifetime; +} diff --git a/tests/Integration/ApiPlatform/ApiClientEndpointTest.php b/tests/Integration/ApiPlatform/ApiClientEndpointTest.php index 0ee637b..c714ad4 100644 --- a/tests/Integration/ApiPlatform/ApiClientEndpointTest.php +++ b/tests/Integration/ApiPlatform/ApiClientEndpointTest.php @@ -51,6 +51,11 @@ public function getProtectedEndpoints(): iterable 'DELETE', '/api-client/1', ]; + + yield 'list endpoint' => [ + 'GET', + '/api-clients', + ]; } public function testAddApiClient(): int @@ -111,6 +116,7 @@ public function testGetApiClient(int $apiClientId): int 'clientId' => 'client_id_test', 'clientName' => 'Client name test', 'description' => 'Client description test', + 'externalIssuer' => null, 'enabled' => true, 'lifetime' => 3600, 'scopes' => [ @@ -161,6 +167,7 @@ public function testUpdateApiClient(int $apiClientId): int 'clientId' => 'client_id_test_updated', 'clientName' => 'Client name test updated', 'description' => 'Client description test updated', + 'externalIssuer' => null, 'enabled' => false, 'lifetime' => 1800, 'scopes' => [ @@ -177,6 +184,7 @@ public function testUpdateApiClient(int $apiClientId): int 'json' => [ 'description' => 'Client description test partially updated', 'lifetime' => 900, + 'externalIssuer' => 'http://not-possible-to-modify', ], ]); self::assertResponseStatusCodeSame(200); @@ -190,6 +198,7 @@ public function testUpdateApiClient(int $apiClientId): int 'clientId' => 'client_id_test_updated', 'clientName' => 'Client name test updated', 'description' => 'Client description test partially updated', + 'externalIssuer' => null, 'enabled' => false, 'lifetime' => 900, 'scopes' => [ @@ -226,6 +235,7 @@ public function testGetUpdatedApiClient(int $apiClientId): int 'clientId' => 'client_id_test_updated', 'clientName' => 'Client name test updated', 'description' => 'Client description test partially updated', + 'externalIssuer' => null, 'enabled' => false, 'lifetime' => 900, 'scopes' => [ @@ -243,6 +253,36 @@ public function testGetUpdatedApiClient(int $apiClientId): int * @depends testGetUpdatedApiClient * * @param int $apiClientId + * + * @return int + */ + public function testListApiClients(int $apiClientId): int + { + $apiClients = $this->listItems('/api-clients', ['api_client_read']); + // Two APi Clients, the one created for test to actually use the API and the one created in the previous test + $this->assertEquals(2, $apiClients['totalItems']); + + // Test content from the second (most recent) api client created + $this->assertEquals( + [ + 'apiClientId' => $apiClientId, + 'clientId' => 'client_id_test_updated', + 'clientName' => 'Client name test updated', + 'description' => 'Client description test partially updated', + 'externalIssuer' => null, + 'enabled' => false, + 'lifetime' => 900, + ], + $apiClients['items'][1], + ); + + return $apiClientId; + } + + /** + * @depends testListApiClients + * + * @param int $apiClientId */ public function testDeleteApiClient(int $apiClientId): void {