Skip to content

Commit 2fd1c35

Browse files
authored
Merge pull request api-platform#1339 from antograssiot/property-filter-whitelist
[2.1] Allow to specify a whitelist of properties for Property Filter
2 parents 12871f3 + 82ea3b7 commit 2fd1c35

File tree

5 files changed

+128
-2
lines changed

5 files changed

+128
-2
lines changed

features/serializer/property_filter.feature

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,98 @@ Feature: Filter with serialization attributes on items and collections
168168
}
169169
"""
170170

171+
Scenario: Get a collection of resources by attributes foo, bar, group.baz and group.qux
172+
When I send a "GET" request to "/dummy_properties?whitelisted_properties[]=foo&whitelisted_properties[]=bar&whitelisted_properties[group][]=baz"
173+
Then the response status code should be 200
174+
And the response should be in JSON
175+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
176+
And the JSON should be valid according to this schema:
177+
"""
178+
{
179+
"type": "object",
180+
"properties": {
181+
"@context": {"pattern": "^/contexts/DummyProperty$"},
182+
"@id": {"pattern": "^/dummy_properties$"},
183+
"@type": {"pattern": "^hydra:Collection$"},
184+
"hydra:member": {
185+
"type": "array",
186+
"items": [
187+
{
188+
"type": "object",
189+
"properties": {
190+
"@id": {},
191+
"@type": {},
192+
"foo": {},
193+
"group": {
194+
"type": "object",
195+
"properties": {
196+
"@id": {},
197+
"@type": {},
198+
"baz": {}
199+
},
200+
"additionalProperties": false,
201+
"required": ["@id", "@type", "baz"]
202+
}
203+
},
204+
"additionalProperties": false,
205+
"required": ["@id", "@type", "foo"]
206+
},
207+
{
208+
"type": "object",
209+
"properties": {
210+
"@id": {},
211+
"@type": {},
212+
"foo": {},
213+
"group": {
214+
"type": "object",
215+
"properties": {
216+
"@id": {},
217+
"@type": {},
218+
"baz": {}
219+
},
220+
"additionalProperties": false,
221+
"required": ["@id", "@type", "baz"]
222+
}
223+
},
224+
"additionalProperties": false,
225+
"required": ["@id", "@type", "foo"]
226+
},
227+
{
228+
"type": "object",
229+
"properties": {
230+
"@id": {},
231+
"@type": {},
232+
"foo": {},
233+
"group": {
234+
"type": "object",
235+
"properties": {
236+
"@id": {},
237+
"@type": {},
238+
"baz": {}
239+
},
240+
"additionalProperties": false,
241+
"required": ["@id", "@type"]
242+
}
243+
},
244+
"additionalProperties": false,
245+
"required": ["@id", "@type", "foo"]
246+
}
247+
],
248+
"additionalItems": false,
249+
"maxItems": 3,
250+
"minItems": 3
251+
},
252+
"hydra:view": {
253+
"type": "object",
254+
"properties": {
255+
"@id": {"pattern": "^/dummy_properties\\?whitelisted_properties%5B%5D=foo&whitelisted_properties%5B%5D=bar&whitelisted_properties%5Bgroup%5D%5B%5D=baz&page=1$"},
256+
"@type": {"pattern": "^hydra:PartialCollectionView$"}
257+
}
258+
}
259+
}
260+
}
261+
"""
262+
171263
Scenario: Get a collection of resources by attributes empty
172264
When I send a "GET" request to "/dummy_properties?properties[]=&properties[group][]="
173265
Then the response status code should be 200

src/Serializer/Filter/PropertyFilter.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ final class PropertyFilter implements FilterInterface
2424
{
2525
private $overrideDefaultProperties;
2626
private $parameterName;
27+
private $whitelist;
2728

28-
public function __construct(string $parameterName = 'properties', bool $overrideDefaultProperties = false)
29+
public function __construct(string $parameterName = 'properties', bool $overrideDefaultProperties = false, array $whitelist = null)
2930
{
3031
$this->overrideDefaultProperties = $overrideDefaultProperties;
3132
$this->parameterName = $parameterName;
33+
$this->whitelist = $whitelist;
3234
}
3335

3436
/**
@@ -40,6 +42,10 @@ public function apply(Request $request, bool $normalization, array $attributes,
4042
return;
4143
}
4244

45+
if (null !== $this->whitelist) {
46+
$properties = array_intersect_key($this->whitelist, $properties);
47+
}
48+
4349
if (!$this->overrideDefaultProperties && isset($context['attributes'])) {
4450
$properties = array_merge_recursive((array) $context['attributes'], $properties);
4551
}

tests/Fixtures/TestBundle/Entity/DummyProperty.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
* "normalization_context"={"groups"={"dummy_read"}},
2929
* "denormalization_context"={"groups"={"dummy_write"}},
3030
* "filters"={
31-
* "dummy_property.property"
31+
* "dummy_property.property",
32+
* "dummy_property.whitelist_property"
3233
* }
3334
* })
3435
*/

tests/Fixtures/app/config/config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ services:
147147
parent: 'api_platform.serializer.property_filter'
148148
tags: [ { name: 'api_platform.filter', id: 'dummy_property.property' } ]
149149

150+
app.entity.filter.dummy_property.whitelist_property:
151+
parent: 'api_platform.serializer.property_filter'
152+
arguments: [ 'whitelisted_properties', false, ['foo', {'group': ['baz', 'qux']}] ]
153+
tags: [ { name: 'api_platform.filter', id: 'dummy_property.whitelist_property' } ]
154+
150155
app.entity.filter.dummy_group.group:
151156
parent: 'api_platform.serializer.group_filter'
152157
tags: [ { name: 'api_platform.filter', id: 'dummy_group.group' } ]

tests/Serializer/Filter/PropertyFilterTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ public function testApplyWithoutPropertiesInRequest()
5454
$this->assertEquals(['attributes' => ['foo', 'bar']], $context);
5555
}
5656

57+
public function testApplyWithPropertiesWhitelist()
58+
{
59+
$request = new Request(['properties' => ['foo', 'bar', 'group' => ['baz' => ['baz', 'qux'], 'qux']]]);
60+
$context = ['attributes' => ['qux']];
61+
62+
$propertyFilter = new PropertyFilter('properties', false, ['foo', 'group' => ['baz' => ['qux']]]);
63+
$propertyFilter->apply($request, true, [], $context);
64+
65+
$this->assertEquals(['attributes' => ['qux', 'foo', 'group' => ['baz' => ['qux']]]], $context);
66+
}
67+
68+
public function testApplyWithoutPropertiesWhitelistWithOverriding()
69+
{
70+
$request = new Request(['properties' => ['foo', 'bar', 'baz']]);
71+
$context = ['attributes' => ['qux']];
72+
73+
$propertyFilter = new PropertyFilter('properties', true, ['foo', 'baz']);
74+
$propertyFilter->apply($request, true, [], $context);
75+
76+
$this->assertEquals(['attributes' => ['foo', 'baz']], $context);
77+
}
78+
5779
public function testApplyWithInvalidPropertiesInRequest()
5880
{
5981
$request = new Request(['properties' => 'foo,bar,baz']);

0 commit comments

Comments
 (0)