Skip to content

Comments

[PropertyFilter] Fix whitelist comparison#1379

Merged
dunglas merged 2 commits intoapi-platform:2.1from
antograssiot:property-filter
Oct 9, 2017
Merged

[PropertyFilter] Fix whitelist comparison#1379
dunglas merged 2 commits intoapi-platform:2.1from
antograssiot:property-filter

Conversation

@antograssiot
Copy link
Contributor

Q A
Bug fix? yes
New feature? yes
BC breaks? yes
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR to come

I'm using the GroupFilter with whitelist since it got merged but never really tried/used the ProprertyFilter whitelist in an application and just relied on the tests I wrote initially. When trying to use it in a real application last week I noticed that the behavior was not the expected one.

An example to reproduce the issue is to whitelist a foo property. Then when requesting /resource?properties[]=bar, foowill be present in the serialized entity.

Another thing I've been trying to fix is the filter declaration and it might be considered as a BC break but given the actual implementation is unusable, I'm pretty sure that we would know if someone would have tried/implemented it already.
The thing is that the array containing properties has numeric and non-numeric keys like:

[
    'foo',
    'bar',
    'group' => ['baz, 'fuz'],
]

I'm no expert but I didn't found a way to generate an array of this kind in yml. The old given example was generating the following:

[
    'foo',
    'bar',
    ['group' => ['baz, 'fuz']],
]

So in order to ease the array intersection, I went to a whitelist definition of the following form

['foo', 'bar', 'group.baz', 'group.fuz']

Even if this is sort of a a BC break, the actual implementation has no chance to work, and using this format will be more inline with the other filter.

*
* @return array an array containing all of the values in array1 recursively whose values exist in array2
*/
private function intersectArrayRecursive(array $array1, array $array2): array
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function and partitionAssocAndNumericArrayKeys() may not really fit into this class, if you want me to extract them into some generic utils class, let me know...

Copy link
Member

@meyerbaptiste meyerbaptiste Oct 2, 2017

Choose a reason for hiding this comment

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

Huum, what about something easier (should be tested):

private function getProperties(array $properties, array $whitelist = null)
{
    $whitelist = $whitelist ?? $this->whitelist;
    $result = [];

    foreach ($properties as $key => $value) {
        if (is_numeric($key)) {
            if (in_array($value, $whitelist, true)) {
                $result[] = $value;
            }

            continue;
        }

        if (isset($whitelist[$key]) && is_array($value) && $recursiveResult = $this->getProperties($value, $whitelist[$key])) {
            $result[$key] = $recursiveResult;
        }
    }

    return $result;
}

@dunglas
Copy link
Member

dunglas commented Sep 18, 2017

Class are not always used with YAML config, IMO it makes sense to support both the old and the new behavior.

@dunglas
Copy link
Member

dunglas commented Sep 18, 2017

Instead of introducing a dotted notation and a lot of code to maintain, I propose to support those syntaxes:

# Use values (no nested properties)

arguments:
  - whitelisted_properties
  - false
  - [ foo, bar ]
# Use keys (support nested properties)

arguments:
  - whitelisted_properties
  - false
  - { foo: ~, bar: [ a, b ] }

This way, it will be in sync with how Symfony handles such cases. WDYT @antograssiot? Can you add the following examples to the test suite and remove the support for the dotted notation?

@meyerbaptiste
Copy link
Member

meyerbaptiste commented Sep 18, 2017

I'm no expert but I didn't found a way to generate an array of this kind in yml.

With Yaml::dump():

foo:
    0: foo
    1: bar
    group: [baz, fuz]

@antograssiot
Copy link
Contributor Author

Thanks @meyerbaptiste, even if I feel dumb now 😛

Ok @dunglas I'll remove the dot notation.
Just so I'm sure I don't miss a point here, if I'm not wrong, I will still need to transform { foo: ~, bar: [ a, b ] } to a php array like

[
'foo',
'bar' => ['a', 'b']
]

because that's this this how the parameters are retrieved from an url like /resources?properties[]=foo&properties['bar][]=a&properties['foo'][]=b

@dunglas
Copy link
Member

dunglas commented Sep 19, 2017

@antograssiot maybe can you juste copy this snippet of mine: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php#L247-L254. It looks it deals with the exact same problem.

@antograssiot
Copy link
Contributor Author

@dunglas I'll be on connected on slack in the next days, would you jump by and ping me maybe so we can figure this out ?

$this->overrideDefaultProperties = $overrideDefaultProperties;
$this->parameterName = $parameterName;
$this->whitelist = $whitelist;
$this->whitelist = null === $whitelist ? null : $this->formatWhitelist($whitelist);
Copy link
Member

Choose a reason for hiding this comment

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

This should be done in your ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Configuration class with the NodeDefinition::beforeNormalization() method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry @meyerbaptiste I don't get this one. From my understanding this is only when using configuration key no ? I mean here it is just services that can be defined multiple time without using any config parameter..

Copy link
Member

Choose a reason for hiding this comment

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

My bad! I spoke too fast and in my head whitelist was a config key... 😛

*
* @return array an array containing all of the values in array1 recursively whose values exist in array2
*/
private function intersectArrayRecursive(array $array1, array $array2): array
Copy link
Member

@meyerbaptiste meyerbaptiste Oct 2, 2017

Choose a reason for hiding this comment

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

Huum, what about something easier (should be tested):

private function getProperties(array $properties, array $whitelist = null)
{
    $whitelist = $whitelist ?? $this->whitelist;
    $result = [];

    foreach ($properties as $key => $value) {
        if (is_numeric($key)) {
            if (in_array($value, $whitelist, true)) {
                $result[] = $value;
            }

            continue;
        }

        if (isset($whitelist[$key]) && is_array($value) && $recursiveResult = $this->getProperties($value, $whitelist[$key])) {
            $result[$key] = $recursiveResult;
        }
    }

    return $result;
}

@antograssiot
Copy link
Contributor Author

Looks cleaner/simpler indeed.
I'll give it a try soon

@antograssiot
Copy link
Contributor Author

@dunglas is it good to go now or you still want me to refractor some parts ?

Copy link
Member

@dunglas dunglas left a comment

Choose a reason for hiding this comment

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

Looks good to me

@dunglas dunglas merged commit ab8d6cc into api-platform:2.1 Oct 9, 2017
@dunglas
Copy link
Member

dunglas commented Oct 9, 2017

Thanks @antograssiot

cr3a7ure added a commit to cr3a7ure/core that referenced this pull request Oct 27, 2017
* GraphQL Query support (api-platform#1358)

* Fix missing service when no Varnish URL is defined

* [PropertyFilter] Fix whitelist comparison (api-platform#1379)

* Remove wrong doc

* Swagger subcollection documentation issue (api-platform#1395)

* Make the requirements configurable

Closes api-platform#1401

* Provide a better exception message and type

Getting "Not found" on a route where you are getting an object can get
really confusing.

* Bump branch alias to 2.2.x-dev

2.1.x-dev is already taken by the 2.1 branch, and this should represent
the next minor version anyway.

* Fix tests

* Reuse PriorityTaggedServiceTrait from symfony

* Improve payload support and remove duplicate code in ConstraintViolationListNormalizer (api-platform#1416)

* Filter Annotation implementation

Parent class filters (needs test)
Support for nested properties
Tests
Fix some comments and remove id=>id in compiler pass

* Throw on abstract data providers / filters

* Remove an unused var

* Remove useless badges

* Enable the coverage

* Fix some quality issues

* Add job to test upstream libs deprecations

If api-platform uses upstream libs in a deprecated way, we should be aware
of it.

* Add job to test upstream libs deprecations

If api-platform uses upstream libs in a deprecated way, we should be aware
of it.

* Fix missing cache tag on empty collections

* Allow plain IDs with `allow_plain_identifiers`

* Fix indentation for GraphQL features.

* Add JSON API basic support (api-platform#785, api-platform#1036, api-platform#1175)

* Clean Behat tests

* Fix tags addition with an empty value

* Document swagger-specific description options

… and where to find them should there be newer ones.

* Fix PHPUnit tests

* Fix missing return statement

* Support & compatibility for PHP7.2

* Add feature to update swagger context for properties

* Generator compat improvements (api-platform#1429)

* Add support for resource names without namespace

* [SF 4.0] Make actions explicitly public

* Allow phpdocumentor/reflection-docblock 4

* fix hydra documentation normalizer with subresources

* Create a base collection normalizer

* Fix request auto-runner

* Update the changelog

* Update changelog
@antograssiot antograssiot deleted the property-filter branch January 3, 2018 17:40
hoangnd25 pushed a commit to hoangnd25/core that referenced this pull request Feb 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants