Problem/Motivation
Right now there is no validation of ?filter params. The variety of filter keys and their combinations might be confusing for developers. All these filter variants are correct:
filter[uuid][value]=1234.
filter[0][condition][path]=uuid&filter[0][condition][value]=1234.
filter[uuid][condition][value]=1234.
filter[uuid][value]=1234&filter[uuid][group]=my_group.
filter[title-filter][condition][path]=title
filter[title-filter][condition][operator]=CONTAINS
filter[title-filter][condition][value]=Foo
# Create an AND and an OR GROUP
filter[and-group][group][conjunction]=AND
filter[or-group][group][conjunction]=OR
# Put the OR group into the AND GROUP
filter[or-group][group][memberOf]=and-group
# Create the admin filter and put it in the AND GROUP
filter[admin-filter][condition][path]=uid.name
filter[admin-filter][condition][value]=admin
filter[admin-filter][condition][memberOf]=and-group
# Create the sticky filter and put it in the OR GROUP
filter[sticky-filter][condition][path]=sticky
filter[sticky-filter][condition][value]=1
filter[sticky-filter][condition][memberOf]=or-group
# Create the promoted filter and put it in the OR GROUP
filter[promote-filter][condition][path]=promote
filter[promote-filter][condition][value]=1
filter[promote-filter][condition][memberOf]=or-group
And so on. The validation should help to avoid painful debugging of invalid filter request.
Proposed resolution
1. Add thorough validation to Drupal\jsonapi\Routing\Param\Filter.
2. Cover all validation cases with Kernel or Unit tests. Should be done in #2852259: Add comprehensive test coverage to \Drupal\jsonapi\Routing\Param\Filter
| Comment | File | Size | Author |
|---|---|---|---|
| #29 | interdiff--2878654--29-26.txt | 13.79 KB | e0ipso |
| #29 | 2878654--filter-validation--29.patch | 23.28 KB | e0ipso |
Comments
Comment #2
spleshkaComment #3
spleshkaComment #4
spleshkaAssigning this one to myself.
Comment #5
spleshkaComment #6
clemens.tolboomIn trying to grasp https://www.drupal.org/docs/8/modules/json-api/collections-filtering-sor... I tried to translate that back into https://en.wikipedia.org/wiki/Backus–Naur_form
For me this helped a little. But I guess if we first spec the filter we can write tests for it easier.
Below a EBNF of the canonical version: filter[{filter id}][{filter type}][{filter property}]={filter value}
filter-id is not completely clear to me. "it can be a string or an integer". What are the string chars? How long can it be?
Both (optional) group is unclear to me in:
- Condition Properties: path, value, operator, (optional) group
- Group Properties: conjunction, (optional) group
memberOf is mentioned on doc page but not in the list of
Comment #7
clemens.tolboomwhy not make that parent instead of that weird repeating [group][group]
Comment #8
spleshkaCouple things to mention:
I don't think that "group" belongs to conditions. It's essentially the same as "memberOf".
It's a good point, but please open a follow-up issue.
What I've found in docs is:
- Condition Properties: path, value, operator, (optional) memberOf
- Group Properties: conjunction, (optional) group, (optional) memberOf
What I've found interesting is that devs can make "condition" query without specifying the "condition" key in the filter query. So these filter queries are identical and both work:
Comment #9
spleshkaLiterally any scalar type will work: int, float, string. I'm assuming that with any set of chars as well, so no limitations here. I've also tried to set this value to a string of ~4k chars and it worked out. So the filter ID length is not limited as well (apart from usual server limitations).
Comment #10
clemens.tolboomAs http://jsonapi.org/format/#fetching-filtering is agnostic I'd love to have our specs for filtering in a rigid way.
I hope we can derive the short forms from the long version.
Drupal core devs or JSON API devs?
Comment #11
spleshkaAnyone who implements the filtering with JSON API.
Comment #12
spleshkaAttaching the first draft. Going to cover the functionality with tests soon. Just thought that someone might have the first round of feedback in the meantime.
Comment #14
spleshkaLooks like "operator" key in "condition" query is NOT mandatory (worth updating the docs).
Comment #15
spleshkaHere's is the final version including tests. Several notes:
1. Slightly refactored dataProvider for valid tests. Hope it's okay :)
2. Treat usage of
filter[group][group]orfilter[condition][group]keys as invalid, because there is no legacy support of those keys in the code, so they are useless and I think frontend apps should know about this.3. We do need to update the documentation to get rid of examples with keys mentioned in the previous item.
4. We do need to mark
operatorkey as optional at https://www.drupal.org/docs/8/modules/json-api/collections-filtering-sor.... As well as that, the list of allowed operators is incomplete: missing "CONTAINS", etc. I'd changeCan be =, <, >, <>, IN, NOT IN, IS NULL, IS NOT NULL or BETWEEN.toSupports any database-compliant operators, for example: =, <, >, <>, IN, NOT IN, IS NULL, IS NOT NULL, BETWEEN, etc.Comment #16
spleshkaOver the night I've thought that it makes sense to add several more tests with valid filters to make sure that the validation doesn't cut them.
Comment #17
clemens.tolboomSo we are not define / specify the filter options? We could say the docs are the specs but my 'bnf' was just a first stab to a specification. I hoped someone would point to a PHP spec tool :p
I hoped for a canonical data provider and a function to create the short version(s) from it.
Why not use the filter URL parameter strings instead of PHP arrays? Makes the comments obsolete.
Comment #18
spleshkaNot sure I understand. Could you please expand?
In some cases we test both short and canonical versions together. Not sure how it's going to work if we provide just a canonical data provider and then reuse it for shorthand version.
The class we're testing accepts only arrays. Conversion of URL strings into arrays is a job of another class and we're not testing it here.
Comment #19
spleshkaAny volunteers to review this? Or ask questions if there are any. I think this patch is important, as it brings in more clarity for contributors regarding filtering system. Additionally, it will be much easier to support backwards compatibility if there will be any changes in the future. So be bold and look into the patch :P
Comment #20
e0ipsoI'm sorry it took too long for this review.
Let's validate the expanded item instead so we can simplify the validation logic.
Comment #21
clemens.tolboomI took quite a tour d.o #2884913: Update link module for array query parameters and Link D7 #2333119: Output broken when using array parameters in query to answer my own question. Why not use parse_str to generate your arrays.
The test code is long due to extra documentation and does not match potential reports submitting their query strings.
I cannot review this patch due to not enough experience yet with filters.
Comment #22
clemens.tolboomI ran into D8 #2885351: Query string duplications wondering whether we should have tests for bad patterns too.
\Drupal\Tests\link\Functional\LinkFieldTest has also some $invalid_external_entries and $invalid_internal_entries.
And related to #21 they use \parse_str in core/lib/Drupal/Core/Url.php:298.
Sorry for the noise ;-)
Comment #23
e0ipso@Spleshka do you think you'll have time to follow up on this soon? If not, there's no problem with that, please unassign the ticket and grab it back when you're available again.
Comment #24
spleshka@e0ipso, I think I'll find the time on this weekend to get it done. Got extremely busy these days, sorry guys for delay.
Comment #25
e0ipso@Spleshka I'm sorry to hear that. I'm cheering for you till the end of your project! I just wanted to make sure you had all you needed for this.
Comment #26
spleshkaHi @e0ipso, thanks for your words :) Attaching the patch with updated validation behavior.
Comment #27
spleshkaI think it should be a separate follow-up. Let's get the basic stuff done first, then I'm happy to enhance it. I'm a big fan of step-by-step improvements :)
Comment #28
e0ipsoThis looks great @Spleshka! I wonder if we can have this kind of validation and test coverage for sort and page as well. Can you create follow up tickets for those?
Comment #29
e0ipsoI ended up doing a refactor and catching a bug with the
IS NULLfilters (they don't require a value).Comment #31
e0ipsoThis is committed. Thanks!
Comment #32
spleshkaThanks Mateu for the fast turn-around & commit!
FYI, added 2 follow-ups: #2887304: [TASK] Implement validation of page param, #2887302: [TASK] Implement validation of sort param