Skip to content

Commit

Permalink
validate maxItems, minItems & uniqueItems filter
Browse files Browse the repository at this point in the history
  • Loading branch information
jdeniau committed Mar 23, 2018
1 parent ab44370 commit 44a8c2e
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 10 deletions.
50 changes: 49 additions & 1 deletion features/filter/filter_validation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,59 @@ Feature: Validate filters based upon filter description
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "enum" must be one of "in-enum, mune-ni"'

@dropSchema
Scenario: Test filter multipleOf
When I am on "/filter_validators?required=foo&required-allow-empty&multiple-of=4"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&multiple-of=3"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "multiple-of" must multiple of 2'

Scenario: Test filter array items csv format minItems
When I am on "/filter_validators?required=foo&required-allow-empty&csv-min-2=a,b"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&csv-min-2=a"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "csv-min-2" must contain more than 2 values'

Scenario: Test filter array items csv format maxItems
When I am on "/filter_validators?required=foo&required-allow-empty&csv-max-3=a,b,c"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&csv-max-3=a,b,c,d"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "csv-max-3" must contain less than 3 values'

Scenario: Test filter array items tsv format minItems
When I am on "/filter_validators?required=foo&required-allow-empty&tsv-min-2=a\tb"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&tsv-min-2=a,b"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "tsv-min-2" must contain more than 2 values'

Scenario: Test filter array items pipes format minItems
When I am on "/filter_validators?required=foo&required-allow-empty&pipes-min-2=a|b"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&pipes-min-2=a,b"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "pipes-min-2" must contain more than 2 values'

Scenario: Test filter array items ssv format minItems
When I am on "/filter_validators?required=foo&required-allow-empty&ssv-min-2=a b"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&ssv-min-2=a,b"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "ssv-min-2" must contain more than 2 values'

@dropSchema
Scenario: Test filter array items unique items
When I am on "/filter_validators?required=foo&required-allow-empty&csv-uniques=a,b"
Then the response status code should be 200

When I am on "/filter_validators?required=foo&required-allow-empty&csv-uniques=a,a"
Then the response status code should be 400
And the JSON node "detail" should be equal to 'Query parameter "csv-uniques" must contain unique values'
13 changes: 4 additions & 9 deletions src/Filter/QueryParameterValidateListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFa
$this->setFilterLocator($filterLocator);

$this->validators = [
new Validator\Required(),
new Validator\ArrayItems(),
new Validator\Bounds(),
new Validator\Length(),
new Validator\Pattern(),
new Validator\Enum(),
new Validator\Length(),
new Validator\MultipleOf(),
new Validator\Pattern(),
new Validator\Required(),
];
}

Expand Down Expand Up @@ -81,10 +82,4 @@ public function onKernelRequest(GetResponseEvent $event)
throw new FilterValidationException($errorList);
}
}

// TODO grouper les filtres required dans une classe
// avoir deux entités, une required, une pour le reste
// maxItems integer See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.2.
// minItems integer See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.3.
// uniqueItems boolean See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.4.
}
82 changes: 82 additions & 0 deletions src/Filter/Validator/ArrayItems.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Filter\Validator;

use Symfony\Component\HttpFoundation\Request;

class ArrayItems implements ValidatorInterface
{
public function validate(string $name, array $filterDescription, Request $request): array
{
if (!$request->query->has($name)) {
return [];
}

$maxItems = $filterDescription['swagger']['maxItems'] ?? null;
$minItems = $filterDescription['swagger']['minItems'] ?? null;
$uniqueItems = $filterDescription['swagger']['uniqueItems'] ?? false;

$errorList = [];

$value = $this->getValue($name, $filterDescription, $request);
$nbItems = count($value);

if (null !== $maxItems && $nbItems > $maxItems) {
$errorList[] = sprintf('Query parameter "%s" must contain less than %d values', $name, $maxItems);
}

if (null !== $minItems && $nbItems < $minItems) {
$errorList[] = sprintf('Query parameter "%s" must contain more than %d values', $name, $minItems);
}

if (true === $uniqueItems && $nbItems > count(array_unique($value))) {
$errorList[] = sprintf('Query parameter "%s" must contain unique values', $name);
}

return $errorList;
}

private function getValue(string $name, array $filterDescription, Request $request): array
{
$value = $request->query->get($name);

if (empty($value) && '0' !== $value) {
return [];
}

if (is_array($value)) {
return $value;
}

$collectionFormat = $filterDescription['swagger']['collectionFormat'] ?? 'csv';

return explode(self::getSeparator($collectionFormat), $value);
}

private static function getSeparator(string $collectionFormat): string
{
switch ($collectionFormat) {
case 'csv':
return ',';
case 'ssv':
return ' ';
case 'tsv':
return '\t';
case 'pipes':
return '|';
default:
throw new \InvalidArgumentException(sprintf('Unkwown collection format %s', $collectionFormat));
}
}
}
2 changes: 2 additions & 0 deletions tests/Fixtures/TestBundle/Entity/FilterValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\ArrayItemsFilter;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\BoundsFilter;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\EnumFilter;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\LengthFilter;
Expand All @@ -31,6 +32,7 @@
*
* @ApiResource(attributes={
* "filters"={
* ArrayItemsFilter::class,
* BoundsFilter::class,
* EnumFilter::class,
* LengthFilter::class,
Expand Down
83 changes: 83 additions & 0 deletions tests/Fixtures/TestBundle/Filter/ArrayItemsFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;

class ArrayItemsFilter extends AbstractFilter
{
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
}

// This function is only used to hook in documentation generators (supported by Swagger and Hydra)
public function getDescription(string $resourceClass): array
{
return [
'csv-min-2' => [
'property' => 'csv-min-2',
'type' => 'array',
'required' => false,
'swagger' => [
'minItems' => 2,
],
],
'csv-max-3' => [
'property' => 'csv-max-3',
'type' => 'array',
'required' => false,
'swagger' => [
'maxItems' => 3,
],
],
'ssv-min-2' => [
'property' => 'ssv-min-2',
'type' => 'array',
'required' => false,
'swagger' => [
'collectionFormat' => 'ssv',
'minItems' => 2,
],
],
'tsv-min-2' => [
'property' => 'tsv-min-2',
'type' => 'array',
'required' => false,
'swagger' => [
'collectionFormat' => 'tsv',
'minItems' => 2,
],
],
'pipes-min-2' => [
'property' => 'pipes-min-2',
'type' => 'array',
'required' => false,
'swagger' => [
'collectionFormat' => 'pipes',
'minItems' => 2,
],
],
'csv-uniques' => [
'property' => 'csv-uniques',
'type' => 'array',
'required' => false,
'swagger' => [
'uniqueItems' => true,
],
],
];
}
}
4 changes: 4 additions & 0 deletions tests/Fixtures/app/config/config_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ services:
arguments: [ '@doctrine' ]
tags: [ 'api_platform.filter' ]

ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\ArrayItemsFilter:
arguments: [ '@doctrine' ]
tags: [ 'api_platform.filter' ]

app.config_dummy_resource.action:
class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Action\ConfigCustom'
arguments: ['@api_platform.item_data_provider']
Expand Down

0 comments on commit 44a8c2e

Please sign in to comment.