Skip to content

More query parameter validation (follow #1692)#1723

Merged
soyuka merged 16 commits into
api-platform:masterfrom
jdeniau:jd-feat-moreQueryParameterValidation
Dec 17, 2019
Merged

More query parameter validation (follow #1692)#1723
soyuka merged 16 commits into
api-platform:masterfrom
jdeniau:jd-feat-moreQueryParameterValidation

Conversation

@jdeniau

@jdeniau jdeniau commented Feb 22, 2018

Copy link
Copy Markdown
Contributor
Q A
Bug fix? kind of
New feature? yes
BC breaks? if filter config was wrongly configured
Deprecations? no
Tests pass? yes
Fixed tickets #1627
License MIT
Doc PR Doc PR will follow if PR is approved

This follows #1692

It implements all other validation filters from OpenApi specification

This might break wrongly configured api though. I see two possible solutions here:

  • make this a [Might break] as we can consider wrongly configured sites a "bug"
  • add a new configuration key validate_filters, default to false, and deprecate it being to false, encouraging people to set it to true, and remove the configuration key in 3.0

@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch 2 times, most recently from ffda2b9 to ff95aeb Compare February 22, 2018 08:19
@jdeniau jdeniau changed the title Jd feat more query parameter validation More query parameter validation (follow #1692) Feb 22, 2018
@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch from ff95aeb to d612479 Compare March 12, 2018 14:26
@jdeniau

jdeniau commented Mar 12, 2018

Copy link
Copy Markdown
Contributor Author

I'm kind of stuck with the error here : I rebased on master, but 4cc6152 now throw an error "debugMessage": "Cannot return null for non-nullable field __InputValue.type.", in features/graphql/introspection.feature, apparently because of tests/Fixtures/TestBundle/Filter/BoundsFilter.php::getDescription return !

Can somebody knowing GraphQL help me ?

@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch 2 times, most recently from 8c9677c to 44a8c2e Compare March 23, 2018 13:21
@jdeniau

jdeniau commented Mar 23, 2018

Copy link
Copy Markdown
Contributor Author

OK I found the issue, it come from the filters containing 'type' => 'number' like:

          'exclusiveMinimum' => [                                                                                                                                            
                  'property' => 'exclusiveMinimum',                                                                                                                              
                  'type' => 'number',                                                                                                                                            
                  'required' => false,                                                                                                                                           
                  'swagger' => [                                                                                                                                                 
                      'minimum' => 5,                                                                                                                                            
                      'exclusiveMinimum' => true,                                                                                                                                
                  ],                                                                                                                                                             
              ],            

If I set "string" or "int", the test does work (but the new tests does not work of course).

What is the real definition behind the type ? I mapped this PR upon the open-api specification, but apparently the GraphQL code use this to get a PHP type ?

I can "hack" and allow "int" and "float" inside this PR but I don't know if it's a good way to make this regarding api-platform point of view.
Another way to do this is to "override" the type in the swagger key, but it would be really redundant and sensible to developer error

Thanks

@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch 2 times, most recently from 948f314 to cbf3f68 Compare March 28, 2018 15:20
@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch 2 times, most recently from d8d205a to 745c431 Compare April 7, 2018 13:35
@soyuka

soyuka commented May 24, 2018

Copy link
Copy Markdown
Member

I've put myself a reminder to review this, let's focus on #1692 first.

@soyuka

soyuka commented Jun 4, 2018

Copy link
Copy Markdown
Member

@jdeniau could you rebase this? Thanks!

@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch from 745c431 to abbd47d Compare June 4, 2018 09:25
@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch 6 times, most recently from ce49bb9 to 9b07b3a Compare June 21, 2018 13:48
@jdeniau

jdeniau commented Jun 21, 2018

Copy link
Copy Markdown
Contributor Author

@dunglas @meyerbaptiste @soyuka Everything should be good for me, except the problem with "number" type that I mentionned earlier.

@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch from 9b07b3a to c7fffe6 Compare June 21, 2018 13:53
@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch from c7fffe6 to 1622e45 Compare December 26, 2018 14:06
@jdeniau

jdeniau commented Dec 26, 2018

Copy link
Copy Markdown
Contributor Author

I finally got time to rework on that, and made the same choice as defined in

if (Type::BUILTIN_TYPE_FLOAT === $type) {
return ['type' => 'number'];
}
, thus if type is documented as float, it will be converted to number for openapi.

This way, it's more "PHP-friendly".

@jdeniau
jdeniau force-pushed the jd-feat-moreQueryParameterValidation branch from c99d8eb to 3ec427e Compare December 4, 2019 22:34
@jdeniau

jdeniau commented Dec 5, 2019

Copy link
Copy Markdown
Contributor Author

@TracKer @flug rebase done.
The only failing test is also failing on master branch

@jdeniau
jdeniau requested review from alanpoulain and soyuka December 5, 2019 21:46

@soyuka soyuka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one, we should merge this as a new feature! WDYT @api-platform/core-team

@soyuka
soyuka merged commit 2e491f7 into api-platform:master Dec 17, 2019
@soyuka

soyuka commented Dec 17, 2019

Copy link
Copy Markdown
Member

Let's merge to avoid further conflicts, we can always come back to some parts of the code if there's smth to change. Thanks a lot @jdeniau for the work!

@jdeniau

jdeniau commented Dec 17, 2019

Copy link
Copy Markdown
Contributor Author

@soyuka Thanks a lot 🙏

If there is anything to change, I will try to be as quick as possible !

@norkunas

Copy link
Copy Markdown
Contributor

Does this allows to configure a SearchFilter with some property as required?

@jdeniau

jdeniau commented Dec 17, 2019

Copy link
Copy Markdown
Contributor Author

@norkunas Yes you can.

See https://github.com/jdeniau/api-platform-core/blob/3ec427e85ccaeff23aca620335bdd0b7c5da3ded/tests/Fixtures/TestBundle/Filter/RequiredFilter.php to do how.

For the record this, the required is operational this since 2.3.0. This PR adds required allowEmptyValue features and all swagger specifications (array items, maximum, minimum, enum, minLength, maxLength, multipleOf, pattern)

@norkunas

Copy link
Copy Markdown
Contributor

Looking at example I'd need to create a custom filter class which would apply the "required" as true, yes? Instead I'd expect something like:

/**
 * @ApiFilter(
 *     SearchFilter::class,
 *     properties={
 *         "offer.id": { "strategy": "exact", "required": true }
 *    }
 * )
 */
class Entity {}

@soyuka

soyuka commented Dec 20, 2019

Copy link
Copy Markdown
Member

I guess we need docs now :D

@jdeniau

jdeniau commented Dec 20, 2019 via email

Copy link
Copy Markdown
Contributor Author

@kovlcode

kovlcode commented Jan 28, 2020

Copy link
Copy Markdown

How to use it? Is there an example?
I need SearchFilter::class with required=true. I would also like to dynamically set required = true (dependent on user role). How can i do this?

my temporaty bad solution:


/**
 * @ApiResource(
 *     // ... rules
 * )
 * @ApiFilter(SearchRequiredFilter::class, properties={"foo": "exact", "bar": "exact"})
 * @ORM\Entity(repositoryClass="App\Repository\ProjectChatRepository")
 */
class User
{ ... }

and SearchRequiredFilter class:

//src/Filter
<?php

declare(strict_types=1);

namespace App\Filter;

// ... many use ...

class SearchRequiredFilter extends SearchFilter
{
    /**
     * @var Security
     */
    private $security;

    public function __construct(
        Security $security,
        // ... parent params
    ) {
        parent::__construct(
           // parents property
        );

        $this->security = $security;
    }

    public function getDescription(string $resourceClass): array
    {
        $description = parent::getDescription($resourceClass);

        foreach ($description as &$options) {
            if (!$options['is_collection']
                && $this->security->isGranted('ROLE_USER')
            ) {
                $options['required'] = true;
            }
        }

        return $description;
    }
}

I understand this is a very bad solution, but how to do my task correctly?

P.S.
I also noticed problem $this->security->isGranted('ROLE_USER') not work and $this->security->getUser() === null. Is this because the filter is triggered before authorization?

@jdeniau

jdeniau commented Jan 28, 2020 via email

Copy link
Copy Markdown
Contributor Author

@efpapado

Copy link
Copy Markdown

Would it be possible to run a custom validation callback on a filter that is not applied to any entity property?
So, like binding the validator class to the filter itself, and not to any entity property.

@jdeniau

jdeniau commented Feb 25, 2020

Copy link
Copy Markdown
Contributor Author

@efpapado for the moment, there are only "swagger" filter validators. (See https://github.com/api-platform/core/blob/master/src/Filter/QueryParameterValidator.php#L35-L43 for more informations)

If you want to run a custom callback in a custom filter file, I think you can easily do something like that:

class FooFilter implements FilterInterface
{
      public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
    {
        $this->throwIfSomethingIsWrong();

        // normal filter behaviour
    }
}

@efpapado

Copy link
Copy Markdown

Thank you so much!!! I wasn't aware of the apply method because it's not in the FilterInterface :)

@jdeniau

jdeniau commented Feb 25, 2020

Copy link
Copy Markdown
Contributor Author

@efpapado You're welcome !

For the record, it is not available in the base ApiPlatform\Core\Api\FilterInterface, but it is available in the doctrine bridge ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\FilterInterface.

It is mentionned in the documentation about custom filter, but in the documentation, the easier AbstractContextAwareFilter class is prefered for it's simplicity.

Theoretically, you can hook into both apply or filterProperty to throw an error whenever you want.

@jdeniau
jdeniau deleted the jd-feat-moreQueryParameterValidation branch March 2, 2020 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants