mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-05 20:57:12 +00:00
[ApiBundle] Extract validation logic from GetStatisticsAction controller
This commit is contained in:
parent
6e71fe259f
commit
2296ce7db9
17 changed files with 613 additions and 162 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
35
src/Sylius/Bundle/ApiBundle/Validator/Constraints/Code.php
Normal file
35
src/Sylius/Bundle/ApiBundle/Validator/Constraints/Code.php
Normal 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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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()),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue