Skip to content

Commit

Permalink
Add validation constraint on ApiClient and add related tests to check…
Browse files Browse the repository at this point in the history
… validation error messages
  • Loading branch information
jolelievre committed Dec 31, 2024
1 parent 5a7efc2 commit bb3093e
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/ApiPlatform/Resources/ApiClient/ApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\ApiClientSettings;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\AddApiClientCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\DeleteApiClientCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\EditApiClientCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientConstraintException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Query\GetApiClientForEditing;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSCreate;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSDelete;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSGet;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSPartialUpdate;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Constraints as Assert;

#[ApiResource(
operations: [
Expand All @@ -51,8 +55,9 @@
),
new CQRSCreate(
uriTemplate: '/api-client',
validationContext: ['groups' => ['Default', 'Create']],
CQRSCommand: AddApiClientCommand::class,
scopes: ['api_client_write']
scopes: ['api_client_write'],
),
new CQRSPartialUpdate(
uriTemplate: '/api-client/{apiClientId}',
Expand All @@ -63,23 +68,34 @@
),
],
normalizationContext: ['skip_null_values' => false],
exceptionToStatus: [ApiClientNotFoundException::class => 404],
exceptionToStatus: [
ApiClientNotFoundException::class => Response::HTTP_NOT_FOUND,
ApiClientConstraintException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
],
)]
class ApiClient
{
#[ApiProperty(identifier: true)]
public int $apiClientId;

#[Assert\NotBlank(groups: ['Create'])]
#[Assert\Length(min: 1, max: ApiClientSettings::MAX_CLIENT_ID_LENGTH)]
public string $clientId;

#[Assert\NotBlank(groups: ['Create'])]
#[Assert\Length(min: 1, max: ApiClientSettings::MAX_CLIENT_NAME_LENGTH)]
public string $clientName;

#[Assert\Length(max: ApiClientSettings::MAX_DESCRIPTION_LENGTH)]
public string $description;

public ?string $externalIssuer;

#[Assert\NotBlank(groups: ['Create'])]
public bool $enabled;

#[Assert\NotBlank(groups: ['Create'])]
#[Assert\Positive]
public int $lifetime;

public array $scopes;
Expand Down
55 changes: 55 additions & 0 deletions tests/Integration/ApiPlatform/ApiClientEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@

namespace PsApiResourcesTest\Integration\ApiPlatform;

use PrestaShop\PrestaShop\Core\Domain\ApiClient\ApiClientSettings;
use PrestaShop\PrestaShop\Core\Util\String\RandomString;
use Symfony\Component\HttpFoundation\Response;

class ApiClientEndpointTest extends ApiTestCase
{
public static function setUpBeforeClass(): void
Expand Down Expand Up @@ -322,4 +326,55 @@ public function testDeleteApiClient(int $apiClientId): void
]);
self::assertResponseStatusCodeSame(404);
}

public function testCreateInvalidApiClient(): void
{
$bearerToken = $this->getBearerToken(['api_client_write']);
$response = static::createClient()->request('POST', '/api-client', [
'auth_bearer' => $bearerToken,
'json' => [
'clientId' => '',
'clientName' => '',
'description' => RandomString::generate(ApiClientSettings::MAX_DESCRIPTION_LENGTH + 1),
'lifetime' => 0,
],
]);
self::assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
// Get content without throwing exception since an error occurred, but we expected it
$decodedResponse = json_decode($response->getContent(false), true);
$this->assertNotFalse($decodedResponse);
$this->assertIsArray($decodedResponse);

$expectedErrors = [
[
'propertyPath' => 'clientId',
'message' => 'This value should not be blank.',
],
[
'propertyPath' => 'clientId',
'message' => 'This value is too short. It should have 1 character or more.',
],
[
'propertyPath' => 'clientName',
'message' => 'This value should not be blank.',
],
[
'propertyPath' => 'clientName',
'message' => 'This value is too short. It should have 1 character or more.',
],
[
'propertyPath' => 'lifetime',
'message' => 'This value should be positive.',
],
[
'propertyPath' => 'enabled',
'message' => 'This value should not be blank.',
],
[
'propertyPath' => 'description',
'message' => sprintf('This value is too long. It should have %d characters or less.', ApiClientSettings::MAX_DESCRIPTION_LENGTH),
],
];
$this->assertValidationErrors($decodedResponse, $expectedErrors);
}
}
37 changes: 37 additions & 0 deletions tests/Integration/ApiPlatform/ApiTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,43 @@ protected function prepareUploadedFile(string $assetFilePath): UploadedFile
return new UploadedFile($tmpUploadedImagePath, basename($assetFilePath));
}

protected function assertValidationErrors(array $responseErrors, array $expectedErrors): void
{
foreach ($responseErrors as $errorDetail) {
$this->assertArrayHasKey('propertyPath', $errorDetail);
$this->assertArrayHasKey('message', $errorDetail);
$this->assertArrayHasKey('code', $errorDetail);

$errorFound = false;
foreach ($expectedErrors as $expectedError) {
if (
(empty($expectedError['message']) || $expectedError['message'] === $errorDetail['message'])
&& (empty($expectedError['propertyPath']) || $expectedError['propertyPath'] === $errorDetail['propertyPath'])
) {
$errorFound = true;
break;
}
}

$this->assertTrue($errorFound, 'Found error that was not expected: ' . var_export($errorDetail, true));
}

foreach ($expectedErrors as $expectedError) {
$errorFound = false;
foreach ($responseErrors as $errorDetail) {
if (
(empty($expectedError['message']) || $expectedError['message'] === $errorDetail['message'])
&& (empty($expectedError['propertyPath']) || $expectedError['propertyPath'] === $errorDetail['propertyPath'])
) {
$errorFound = true;
break;
}
}

$this->assertTrue($errorFound, 'Could not find expected error: ' . var_export($expectedError, true));
}
}

protected static function createApiClient(array $scopes = [], int $lifetime = 10000): void
{
$command = new AddApiClientCommand(
Expand Down

0 comments on commit bb3093e

Please sign in to comment.