[ApiBundle] Extract validation logic from GetStatisticsAction controller

This commit is contained in:
Rafikooo 2023-12-26 03:17:53 +01:00
parent 6e71fe259f
commit 2296ce7db9
No known key found for this signature in database
GPG key ID: 4A26D8327BC2442B
17 changed files with 613 additions and 162 deletions

View file

@ -13,7 +13,9 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Controller;
use Sylius\Bundle\ApiBundle\Exception\ChannelNotFoundException;
use Sylius\Bundle\ApiBundle\Query\GetStatistics;
use Sylius\Bundle\ApiBundle\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@ -21,174 +23,74 @@ use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\HandleTrait;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Constraints as BaseAssert;
use Symfony\Component\Validator\Validator\ValidatorInterface;
final class GetStatisticsAction
{
use HandleTrait;
/** @var array<array{queryParameter: string, message: string}> */
private array $violations = [];
/** @var array<string> */
private array $requiredParameters = [
'channelCode' => 'string',
'startDate' => 'dateTime',
'dateInterval' => 'dateInterval',
'endDate' => 'dateTime',
];
public function __construct(
MessageBusInterface $queryBus,
private SerializerInterface $serializer,
private ValidatorInterface $validator,
) {
$this->messageBus = $queryBus;
}
/**
* @throws \Throwable
*/
public function __invoke(Request $request): Response
{
$this->validateRequiredParameters($request);
$constraint = new BaseAssert\Collection([
'channelCode' => new Assert\Code(),
'startDate' => new BaseAssert\DateTime('Y-m-d\TH:i:s', message: 'sylius.date_time.invalid'),
'dateInterval' => new Assert\DateInterval(),
'endDate' => new BaseAssert\DateTime('Y-m-d\TH:i:s', message: 'sylius.date_time.invalid'),
]);
$parameters = $request->query->all();
$violations = $this->validator->validate($request->query->all(), $constraint);
if (count($this->violations) > 0) {
if (count($violations) > 0) {
return new JsonResponse(
data: $this->serializer->serialize($this->violations, 'json'),
data: $this->serializer->serialize($violations, 'json'),
status: Response::HTTP_BAD_REQUEST,
json: true,
);
}
$parameters = $request->query->all();
$period = new \DatePeriod(
new \DateTimeImmutable($parameters['startDate']),
new \DateInterval($parameters['dateInterval']),
new \DateTimeImmutable($parameters['endDate']),
);
try {
$this->validateEndDateIsNotBeforeStartDate($period);
$this->validateEndDateAgainstInterval($period);
} catch (\InvalidArgumentException $exception) {
$violations = $this->validator->validate($period, new Assert\DatePeriod());
if (count($violations) > 0) {
return new JsonResponse(
data: $this->serializer->serialize(['message' => $exception->getMessage()], 'json'),
data: $this->serializer->serialize($violations, 'json'),
status: Response::HTTP_BAD_REQUEST,
json: true,
);
}
$query = new GetStatistics($period, $parameters['channelCode']);
try {
$result = $this->handle($query);
$result = $this->handle(new GetStatistics($period, $parameters['channelCode']));
$status = Response::HTTP_OK;
} catch (HandlerFailedException $exception) {
return new JsonResponse(
data: $this->serializer->serialize(['message' => $exception->getMessage()], 'json'),
status: Response::HTTP_NOT_FOUND,
json: true,
);
}
$exception = $exception->getPrevious();
$result = ['message' => $exception->getMessage()];
return new JsonResponse(data: $this->serializer->serialize($result, 'json'), json: true);
}
private function validateRequiredParameters(Request $request): void
{
foreach ($this->requiredParameters as $parameterName => $parameterType) {
$parameter = $request->query->get($parameterName);
if ($parameter === null) {
$this->addViolation($parameterName, sprintf('Parameter "%s" is required.', $parameterName));
continue;
}
if (empty($parameter)) {
$this->addViolation($parameterName, sprintf('Parameter "%s" cannot be empty.', $parameterName));
continue;
}
if ($parameterType === 'dateTime' && !$this->isISO8601DateTimeWithNoTimezone($parameter)) {
$this->addViolation($parameterName, sprintf(
'Parameter "%s" must be a valid ISO8601 date time string without timezone.',
$parameterName,
));
} elseif ($parameterType === 'dateInterval' && !$this->isValidInterval($parameter)) {
$this->addViolation($parameterName, sprintf(
'Parameter "%s" must be a valid DateInterval string.',
$parameterName,
));
} elseif ($parameterType === 'int' && filter_var($parameter, \FILTER_VALIDATE_INT) === false) {
$this->addViolation($parameterName, sprintf(
'Parameter "%s" must be an integer.',
$parameterName,
));
} elseif (!is_string($parameter) || is_numeric($parameter)) {
$this->addViolation($parameterName, sprintf(
'Parameter "%s" must be a string.',
$parameterName,
));
if ($exception instanceof ChannelNotFoundException) {
$status = Response::HTTP_NOT_FOUND;
} else {
throw $exception;
}
}
}
private function isISO8601DateTimeWithNoTimezone(?string $dateTime = null): bool
{
return preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/', $dateTime) === 1;
}
private function isValidInterval(?string $interval = null): bool
{
try {
new \DateInterval($interval);
} catch (\Exception) {
return false;
}
return true;
}
/**
* Validates that the end date is a multiple of the interval.
* The end date is adjusted by subtracting one second to make it inclusive (closed interval).
* If the adjusted end date does not match the provided end date, an exception is thrown.
*/
private function validateEndDateAgainstInterval(\DatePeriod $datePeriod): void
{
$currentDate = clone $datePeriod->getStartDate();
$endDate = $datePeriod->getEndDate();
$interval = $datePeriod->getDateInterval();
while ($currentDate <= $endDate) {
$currentDate = $currentDate->add($interval);
}
/** We shift to make closed interval. */
$intervalEndDate = $currentDate->modify('-1 second');
if ($intervalEndDate != $endDate) {
throw new \InvalidArgumentException(sprintf(
sprintf(
'End date "%s" must be multiple of interval, expected "%s"',
$endDate->format('Y-m-d H:i:s'),
$intervalEndDate->format('Y-m-d H:i:s'),
),
));
}
}
private function validateEndDateIsNotBeforeStartDate(\DatePeriod $period): void
{
if ($period->getStartDate() > $period->getEndDate()) {
throw new \InvalidArgumentException(sprintf(
'End date "%s" must be after start date "%s"',
$period->getEndDate()->format('Y-m-d H:i:s'),
$period->getStartDate()->format('Y-m-d H:i:s'),
));
}
}
private function addViolation(string $parameterName, string $message): void
{
$this->violations[] = ['queryParameter' => $parameterName, 'message' => $message];
return new JsonResponse(data: $this->serializer->serialize($result, 'json'), status: $status, json: true);
}
}

View file

@ -68,6 +68,7 @@
<service id="Sylius\Bundle\ApiBundle\Controller\GetStatisticsAction">
<argument type="service" id="sylius.query_bus" />
<argument type="service" id="serializer" />
<argument type="service" id="validator" />
</service>
</services>
</container>

View file

@ -188,5 +188,17 @@
<argument type="service" id="Sylius\Bundle\ApiBundle\Validator\CatalogPromotion\ForVariantsScopeValidator.inner" />
<argument type="service" id="Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface" />
</service>
<service id="Sylius\Bundle\ApiBundle\Validator\Constraints\DateIntervalValidator">
<tag name="validator.constraint_validator" alias="sylius_api_validator_date_interval" />
</service>
<service id="Sylius\Bundle\ApiBundle\Validator\Constraints\EndDateIsNotBeforeStartDateValidator">
<tag name="validator.constraint_validator" alias="sylius_api_validator_date_period_end_date_is_not_before_start_date" />
</service>
<service id="Sylius\Bundle\ApiBundle\Validator\Constraints\EndDateAgainstIntervalValidator">
<tag name="validator.constraint_validator" alias="sylius_api_validator_end_date_against_interval" />
</service>
</services>
</container>

View file

@ -6,10 +6,20 @@ sylius:
is_verified: 'Account with email %email% is currently verified.'
address:
without_country: 'The address without country cannot exist'
code:
invalid: 'The code should contain only letters, numbers, dashes ("-" symbol) and underscores ("_" symbol).'
contact_request:
email_is_invalid: 'The provided email is invalid.'
country:
not_exist: 'The country %countryCode% does not exist.'
date_interval:
empty: 'The date interval cannot be empty.'
invalid: 'The date interval is not valid ISO 8601 interval.'
date_period:
end_date_is_not_before_start_date: 'The end date is not before start date.'
end_date_must_be_multiple_of_interval: 'End date "%givenDate%" must be multiple of interval, expected "%expectedDate%".'
date_time:
invalid: 'The date time is not valid ISO 8601 date time in Y-m-d\TH:i:s format.'
order:
not_empty: 'An empty order cannot be completed.'
payment_method:

View file

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraints\Compound;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Constraints\Type;
/** @experimental */
final class Code extends Compound
{
/**
* @param array<mixed> $options
*/
protected function getConstraints(array $options): array
{
return [
new NotBlank(message: 'sylius.code.not_blank'),
new Type('string'),
new Regex(['pattern' => '/^[\w-]*$/'], message: 'sylius.code.invalid'),
];
}
}

View file

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/** @experimental */
final class DateInterval extends Constraint
{
public string $message = 'sylius.date_interval.invalid';
public function validatedBy(): string
{
return 'sylius_api_validator_date_interval';
}
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;
/** @experimental */
final class DateIntervalValidator extends ConstraintValidator
{
/**
* @param string $value
* @param DateInterval $constraint
*/
public function validate(mixed $value, Constraint $constraint): void
{
Assert::string($value);
Assert::isInstanceOf($constraint, DateInterval::class);
try {
new \DateInterval($value);
} catch (\Exception) {
$this->context->addViolation($constraint->message);
}
}
}

View file

@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraints\Compound;
/** @experimental */
final class DatePeriod extends Compound
{
/**
* @param array<mixed> $options
*/
protected function getConstraints(array $options): array
{
return [
new EndDateIsNotBeforeStartDate(),
new EndDateAgainstInterval(),
];
}
}

View file

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/** @experimental */
final class EndDateAgainstInterval extends Constraint
{
/** @var string */
public $message = 'sylius.date_period.end_date_must_be_multiple_of_interval';
public function validatedBy(): string
{
return 'sylius_api_validator_end_date_against_interval';
}
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}

View file

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;
/** @experimental */
final class EndDateAgainstIntervalValidator extends ConstraintValidator
{
/**
* @param \DatePeriod $value
* @param EndDateAgainstInterval $constraint
*
* Validates that the end date is a multiple of the interval.
* The end date is adjusted by subtracting one second to make it inclusive (closed interval).
* If the adjusted end date does not match the provided end date, an exception is thrown.
*/
public function validate(mixed $value, Constraint $constraint): void
{
Assert::isInstanceOf($value, \DatePeriod::class);
Assert::isInstanceOf($constraint, EndDateAgainstInterval::class);
$currentDate = clone $value->getStartDate();
$endDate = $value->getEndDate();
$interval = $value->getDateInterval();
while ($currentDate <= $endDate) {
$currentDate = $currentDate->add($interval);
}
/** We shift to make closed interval. */
$intervalEndDate = $currentDate->modify('-1 second');
if ($intervalEndDate != $endDate) {
$this
->context
->buildViolation($constraint->message)
->setParameter('%givenDate%', $endDate->format('Y-m-d H:i:s'))
->setParameter('%expectedDate%', $intervalEndDate->format('Y-m-d H:i:s'))
->addViolation()
;
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/** @experimental */
final class EndDateIsNotBeforeStartDate extends Constraint
{
public string $message = 'sylius.date_period.end_date_is_not_before_start_date';
public function validatedBy(): string
{
return 'sylius_api_validator_date_period_end_date_is_not_before_start_date';
}
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}

View file

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;
/** @experimental */
final class EndDateIsNotBeforeStartDateValidator extends ConstraintValidator
{
/**
* @param \DatePeriod $value
* @param EndDateIsNotBeforeStartDate $constraint
*/
public function validate(mixed $value, Constraint $constraint): void
{
Assert::isInstanceOf($value, \DatePeriod::class);
Assert::isInstanceOf($constraint, EndDateIsNotBeforeStartDate::class);
if ($value->getStartDate() > $value->getEndDate()) {
$this->context->addViolation($constraint->message);
}
}
}

View file

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Validator\Constraints\DateInterval;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
final class DateIntervalValidatorSpec extends ObjectBehavior
{
function it_is_a_constraint_validator(): void
{
$this->shouldImplement(ConstraintValidatorInterface::class);
}
function it_throws_an_exception_if_value_is_not_an_instance_of_date_interval(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [new \stdClass(), new DateInterval()])
;
}
function it_throws_an_exception_if_constraint_is_not_an_instance_of_date_interval(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [
new \DateInterval('P1D'),
new class() extends Constraint {
},
])
;
}
function it_adds_violation_if_date_interval_format_is_invalid(ExecutionContextInterface $executionContext): void
{
$this->initialize($executionContext);
$constraint = new DateInterval();
$executionContext->addViolation($constraint->message)->shouldBeCalled();
$this->validate('INVALID_DATE_INTERVAL_FORMAT', $constraint);
}
function it_does_not_add_violation_if_date_interval_format_is_valid(
ExecutionContextInterface $executionContext,
): void {
$this->initialize($executionContext);
$constraint = new DateInterval();
$executionContext->addViolation($constraint->message)->shouldNotBeCalled();
$this->validate('P1D', $constraint);
}
}

View file

@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Validator\Constraints\EndDateAgainstInterval;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
final class EndDateAgainstIntervalValidatorSpec extends ObjectBehavior
{
function it_is_a_constraint_validator(): void
{
$this->shouldImplement(ConstraintValidatorInterface::class);
}
function it_throws_an_exception_if_value_is_not_an_instance_of_date_period(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [new \stdClass(), new EndDateAgainstInterval()]);
}
function it_throws_an_exception_if_constraint_is_not_an_instance_of_end_date_against_interval(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [
new \DatePeriod(new \DateTimeImmutable(), new \DateInterval('P1D'), new \DateTimeImmutable()),
new class() extends Constraint {
},
]);
}
function it_adds_violation_if_end_date_is_not_a_multiple_of_interval(
ExecutionContextInterface $executionContext,
ConstraintViolationBuilderInterface $constraintViolationBuilder,
): void {
$this->initialize($executionContext);
$constraint = new EndDateAgainstInterval();
$constraintViolationBuilder
->setParameter('%expectedDate%', '2019-01-02 23:59:59')
->willReturn($constraintViolationBuilder)
;
$constraintViolationBuilder
->setParameter('%givenDate%', '2019-01-02 23:59:58')
->willReturn($constraintViolationBuilder)
;
$constraintViolationBuilder->addViolation()->shouldBeCalled();
$executionContext->buildViolation($constraint->message)->willReturn($constraintViolationBuilder);
$this->validate(
new \DatePeriod(
new \DateTimeImmutable('2019-01-01 00:00:00'),
new \DateInterval('P1D'),
new \DateTimeImmutable('2019-01-02 23:59:58'),
),
$constraint,
);
}
function it_does_not_add_violation_if_end_date_is_a_multiple_of_interval(
ExecutionContextInterface $executionContext,
): void {
$this->initialize($executionContext);
$constraint = new EndDateAgainstInterval();
$executionContext->buildViolation($constraint->message)->shouldNotBeCalled();
$this->validate(
new \DatePeriod(
new \DateTimeImmutable('2019-01-01 00:00:00'),
new \DateInterval('P1D'),
new \DateTimeImmutable('2019-01-03 23:59:59'),
),
$constraint,
);
}
}

View file

@ -0,0 +1,84 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Validator\Constraints\EndDateIsNotBeforeStartDate;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
final class EndDateIsNotBeforeStartDateValidatorSpec extends ObjectBehavior
{
function it_is_a_constraint_validator(): void
{
$this->shouldImplement(ConstraintValidatorInterface::class);
}
function it_throws_an_exception_if_value_is_not_an_instance_of_date_period(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [new \stdClass(), new EndDateIsNotBeforeStartDate()])
;
}
function it_throws_an_exception_if_constraint_is_not_an_instance_of_end_date_is_not_before_start_date(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [
new \DatePeriod(new \DateTimeImmutable(), new \DateInterval('P1D'), new \DateTimeImmutable()),
new class() extends Constraint {
},
])
;
}
function it_adds_violation_if_end_date_is_before_start_date(
ExecutionContextInterface $executionContext,
): void {
$this->initialize($executionContext);
$constraint = new EndDateIsNotBeforeStartDate();
$executionContext->addViolation($constraint->message)->shouldBeCalled();
$this->validate(
new \DatePeriod(
new \DateTimeImmutable('2020-01-01'),
new \DateInterval('P1D'),
new \DateTimeImmutable('2019-01-01'),
),
$constraint,
);
}
function it_does_not_add_violation_if_end_date_is_after_start_date(
ExecutionContextInterface $executionContext,
): void {
$this->initialize($executionContext);
$constraint = new EndDateIsNotBeforeStartDate();
$executionContext->addViolation($constraint->message)->shouldNotBeCalled();
$this->validate(
new \DatePeriod(
new \DateTimeImmutable('2019-01-01'),
new \DateInterval('P1D'),
new \DateTimeImmutable('2020-01-01'),
),
$constraint,
);
}
}

View file

@ -36,8 +36,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
);
$this->assertResponse(
@ -62,8 +61,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
);
$response = $this->client->getResponse();
@ -76,7 +74,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
{
$this->loadFixturesFromFiles([
'product/product_with_many_locales.yaml',
'authentication/api_administrator.yaml'
'authentication/api_administrator.yaml',
]);
$this->client->request(
@ -86,8 +84,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->headerBuilder()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
);
$this->assertResponse(
@ -113,8 +110,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
content: json_encode([
'type' => '/api/v2/admin/product-association-types/similar_products',
'owner' => '/api/v2/admin/products/CUP',
@ -149,8 +145,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
content: json_encode([
'associatedProducts' => [
'/api/v2/admin/products/TANKARD',
@ -217,8 +212,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
content: '{}',
);
@ -248,8 +242,7 @@ final class ProductAssociationsTest extends JsonApiTestCase
->withJsonLdContentType()
->withJsonLdAccept()
->withAdminUserAuthorization('api@example.com')
->build()
,
->build(),
content: json_encode([
'type' => sprintf('/api/v2/admin/product-association-types/%s', $association->getType()->getCode()),
'owner' => sprintf('/api/v2/admin/products/%s', $association->getOwner()->getCode()),

View file

@ -214,24 +214,6 @@ final class StatisticsTest extends JsonApiTestCase
public function invalidQueryParameters(): iterable
{
yield 'invalid channelCode as bool value' => [
'parameters' => [
'channelCode' => true,
'startDate' => '2023-01-01T00:00:00',
'dateInterval' => 'P1M',
'endDate' => '2023-12-31T23:59:59',
],
];
yield 'invalid channelCode as int value' => [
'parameters' => [
'channelCode' => 1,
'startDate' => '2023-01-01T00:00:00',
'dateInterval' => 'P1M',
'endDate' => '2023-12-31T23:59:59',
],
];
yield 'invalid channelCode as float value' => [
'parameters' => [
'channelCode' => 1.1,
@ -276,6 +258,16 @@ final class StatisticsTest extends JsonApiTestCase
'endDate' => '2023-12-31T23:59:59',
],
];
yield 'unexpected parameter' => [
'parameters' => [
'channelCode' => 'WEB',
'startDate' => '2023-01-01T00:00:00',
'dateInterval' => 'P1M1D',
'endDate' => '2023-12-31T23:59:59',
'unexpected_parameter' => 'unexpected_value',
],
];
}
public function invalidPeriods(): iterable