Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More query parameter validation (follow #1692) #1723

Merged

Conversation

jdeniau
Copy link
Contributor

@jdeniau jdeniau commented Feb 22, 2018

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
Copy link
Contributor Author

jdeniau commented Mar 12, 2018

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
Copy link
Contributor Author

jdeniau commented Mar 23, 2018

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
Copy link
Member

soyuka commented May 24, 2018

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

@soyuka
Copy link
Member

soyuka commented Jun 4, 2018

@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
Copy link
Contributor Author

jdeniau commented Jun 21, 2018

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

@jdeniau
Copy link
Contributor Author

jdeniau commented Dec 26, 2018

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
Copy link
Contributor Author

jdeniau commented Dec 5, 2019

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

Copy link
Member

@soyuka soyuka left a comment

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
Copy link
Member

soyuka commented Dec 17, 2019

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
Copy link
Contributor Author

jdeniau commented Dec 17, 2019

@soyuka Thanks a lot 🙏

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

@norkunas
Copy link
Contributor

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

@jdeniau
Copy link
Contributor Author

jdeniau commented Dec 17, 2019

@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
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
Copy link
Member

soyuka commented Dec 20, 2019

I guess we need docs now :D

@jdeniau
Copy link
Contributor Author

jdeniau commented Dec 20, 2019 via email

@kovlcode
Copy link

kovlcode commented Jan 28, 2020

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
Copy link
Contributor Author

jdeniau commented Jan 28, 2020 via email

@efpapado
Copy link

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
Copy link
Contributor Author

jdeniau commented Feb 25, 2020

@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

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

@jdeniau
Copy link
Contributor Author

jdeniau commented Feb 25, 2020

@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.

None yet

10 participants