Skip to content

Commit 36f603a

Browse files
committed
IBX-5135: Added LocationDepth criterion parser
1 parent 006ffbe commit 36f603a

File tree

8 files changed

+348
-4
lines changed

8 files changed

+348
-4
lines changed

src/bundle/Resources/config/input_parsers.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,11 @@ services:
291291
tags:
292292
- { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.internal.LocationQuery }
293293

294+
Ibexa\Rest\Server\Input\Parser\Criterion\Location\Depth:
295+
parent: Ibexa\Rest\Server\Common\Parser
296+
tags:
297+
- { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.internal.criterion.LocationDepth }
298+
294299
Ibexa\Rest\Server\Input\Parser\Criterion\Ancestor:
295300
parent: Ibexa\Rest\Server\Common\Parser
296301
class: Ibexa\Rest\Server\Input\Parser\Criterion\Ancestor
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Rest\Server\Input\Parser\Criterion\Location;
10+
11+
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Location\Depth as DepthCriterion;
12+
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator;
13+
use Ibexa\Contracts\Rest\Exceptions;
14+
use Ibexa\Contracts\Rest\Input\ParsingDispatcher;
15+
use Ibexa\Rest\Input\BaseParser;
16+
17+
final class Depth extends BaseParser
18+
{
19+
private const OPERATORS = [
20+
'IN' => Operator::IN,
21+
'EQ' => Operator::EQ,
22+
'GT' => Operator::GT,
23+
'GTE' => Operator::GTE,
24+
'LT' => Operator::LT,
25+
'LTE' => Operator::LTE,
26+
'BETWEEN' => Operator::BETWEEN,
27+
];
28+
29+
public function parse(array $data, ParsingDispatcher $parsingDispatcher): DepthCriterion
30+
{
31+
if (!array_key_exists('LocationDepth', $data)) {
32+
throw new Exceptions\Parser('Invalid <LocationDepth> format');
33+
}
34+
35+
$criterionData = $data['LocationDepth'];
36+
if (!is_array($criterionData)) {
37+
throw new Exceptions\Parser('Invalid <LocationDepth> format');
38+
}
39+
40+
if (!isset($criterionData['Value'])) {
41+
throw new Exceptions\Parser('Invalid <Value> format');
42+
}
43+
44+
if (
45+
is_string($criterionData['Value'])
46+
&& is_numeric($criterionData['Value'])
47+
&& ((int)$criterionData['Value'] == $criterionData['Value'])
48+
) {
49+
$criterionData['Value'] = (int)$criterionData['Value'];
50+
}
51+
52+
if (!in_array(gettype($criterionData['Value']), ['integer', 'array'], true)) {
53+
throw new Exceptions\Parser('Invalid <Value> format');
54+
}
55+
56+
$value = $criterionData['Value'];
57+
58+
if (!isset($criterionData['Operator'])) {
59+
throw new Exceptions\Parser('Invalid <Operator> format');
60+
}
61+
62+
$operator = $this->getOperator($criterionData['Operator']);
63+
64+
return new DepthCriterion($operator, $value);
65+
}
66+
67+
/**
68+
* Get operator for the given literal name.
69+
*/
70+
private function getOperator(string $operatorName): string
71+
{
72+
$operatorName = strtoupper($operatorName);
73+
if (!isset(self::OPERATORS[$operatorName])) {
74+
throw new Exceptions\Parser(
75+
sprintf(
76+
'Unexpected LocationDepth operator. Expected one of: %s',
77+
implode(', ', array_keys(self::OPERATORS))
78+
)
79+
);
80+
}
81+
82+
return self::OPERATORS[$operatorName];
83+
}
84+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"type": "object",
4+
"additionalProperties": false,
5+
"required": [
6+
"View"
7+
],
8+
"properties": {
9+
"View": {
10+
"type": "object",
11+
"additionalProperties": false,
12+
"required": [
13+
"_media-type",
14+
"_href",
15+
"identifier",
16+
"Query",
17+
"Result"
18+
],
19+
"properties": {
20+
"_media-type": {
21+
"type": "string"
22+
},
23+
"_href": {
24+
"type": "string"
25+
},
26+
"identifier": {
27+
"type": "string"
28+
},
29+
"Query": {
30+
"type": "object",
31+
"properties": {
32+
"_media-type": {
33+
"type": "string"
34+
}
35+
},
36+
"additionalProperties": false,
37+
"required": [
38+
"_media-type"
39+
]
40+
},
41+
"Result": {
42+
"type": "object",
43+
"additionalProperties": false,
44+
"required": [
45+
"_media-type",
46+
"_href",
47+
"count",
48+
"time",
49+
"timedOut",
50+
"maxScore",
51+
"searchHits",
52+
"aggregations"
53+
],
54+
"properties": {
55+
"_media-type": {
56+
"type": "string"
57+
},
58+
"_href": {
59+
"type": "string"
60+
},
61+
"count": {
62+
"type": "number"
63+
},
64+
"time": {
65+
"type": "number"
66+
},
67+
"timedOut": {
68+
"type": "boolean"
69+
},
70+
"maxScore": {
71+
"oneOf": [
72+
{
73+
"type": "number"
74+
}, {
75+
"type": "null"
76+
}
77+
]
78+
},
79+
"searchHits": {
80+
"type": "object",
81+
"additionalProperties": false,
82+
"required": [
83+
"searchHit"
84+
],
85+
"properties": {
86+
"searchHit": {
87+
"type": "array",
88+
"items": {
89+
"type": "object",
90+
"required": [
91+
"_media-type",
92+
"_score",
93+
"_index",
94+
"value"
95+
],
96+
"additionalProperties": false,
97+
"properties": {
98+
"_media-type": {
99+
"type": "string"
100+
},
101+
"_score": {
102+
"type": "number"
103+
},
104+
"_index": {
105+
"type": "string"
106+
},
107+
"value": {
108+
"type": "object",
109+
"required": [
110+
"_media-type",
111+
"Content"
112+
],
113+
"additionalProperties": false,
114+
"properties": {
115+
"_media-type": {
116+
"type": "string"
117+
},
118+
"Content": {
119+
"type": "object"
120+
}
121+
}
122+
}
123+
}
124+
}
125+
}
126+
}
127+
},
128+
"aggregations": {
129+
"type": "array"
130+
}
131+
}
132+
}
133+
}
134+
}
135+
}
136+
}

tests/bundle/Functional/ViewTest.php

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
class ViewTest extends TestCase
1010
{
11+
use ResourceAssertionsTrait;
12+
1113
/**
1214
* Covers POST /views.
1315
*/
14-
public function testViewRequestWithOrStatement()
16+
public function testViewRequestWithOrStatement(): void
1517
{
1618
$fooRemoteId = md5('View test content foo');
1719
$barRemoteId = md5('View test content bar');
@@ -42,17 +44,71 @@ public function testViewRequestWithOrStatement()
4244
$body
4345
);
4446
$response = $this->sendHttpRequest($request);
47+
self::assertHttpResponseCodeEquals($response, 200);
48+
self::assertJsonResponseIsValid($response->getBody()->getContents(), 'View');
4549
$responseData = json_decode($response->getBody(), true);
4650

4751
self::assertEquals(2, $responseData['View']['Result']['count']);
4852
}
4953

54+
/**
55+
* @dataProvider provideForViewTest
56+
*/
57+
public function testCriterions(string $body, string $type): void
58+
{
59+
$request = $this->createHttpRequest(
60+
'POST',
61+
'/api/ibexa/v2/views',
62+
"ViewInput+$type",
63+
'View+json',
64+
$body
65+
);
66+
$response = $this->sendHttpRequest($request);
67+
self::assertHttpResponseCodeEquals($response, 200);
68+
self::assertJsonResponseIsValid($response->getBody()->getContents(), 'View');
69+
$responseData = json_decode($response->getBody(), true);
70+
self::assertGreaterThan(0, $responseData['View']['Result']['count']);
71+
}
72+
73+
/**
74+
* @return iterable<string, array{string, string}>
75+
*/
76+
public function provideForViewTest(): iterable
77+
{
78+
$template = static fn(string $criterion, string $operator, string $format): string => sprintf(
79+
'Criterion: %s / Operator: %s / Format: %s',
80+
$criterion,
81+
strtoupper($operator),
82+
strtoupper($format),
83+
);
84+
85+
yield $template('LocationDepth', 'eq', 'xml') => [
86+
file_get_contents(__DIR__ . '/_input/search/LocationDepth.eq.xml'),
87+
'xml',
88+
];
89+
90+
yield $template('LocationDepth', 'eq', 'json') => [
91+
file_get_contents(__DIR__ . '/_input/search/LocationDepth.eq.json'),
92+
'json',
93+
];
94+
95+
yield $template('LocationDepth', 'in', 'xml') => [
96+
file_get_contents(__DIR__ . '/_input/search/LocationDepth.in.xml'),
97+
'xml',
98+
];
99+
100+
yield $template('LocationDepth', 'in', 'json') => [
101+
file_get_contents(__DIR__ . '/_input/search/LocationDepth.in.json'),
102+
'json',
103+
];
104+
}
105+
50106
/**
51107
* Covers POST /views.
52108
*
53109
* @depends testViewRequestWithOrStatement
54110
*/
55-
public function testViewRequestWithAndStatement()
111+
public function testViewRequestWithAndStatement(): void
56112
{
57113
$fooRemoteId = md5('View test content foo');
58114
$barRemoteId = md5('View test content bar');
@@ -84,10 +140,10 @@ public function testViewRequestWithAndStatement()
84140
$body
85141
);
86142
$response = $this->sendHttpRequest($request);
143+
self::assertHttpResponseCodeEquals($response, 200);
144+
self::assertJsonResponseIsValid($response->getBody()->getContents(), 'View');
87145
$responseData = json_decode($response->getBody(), true);
88146

89147
self::assertEquals(1, $responseData['View']['Result']['count']);
90148
}
91149
}
92-
93-
class_alias(ViewTest::class, 'EzSystems\EzPlatformRestBundle\Tests\Functional\ViewTest');
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"ViewInput": {
3+
"identifier": "TitleView",
4+
"Query": {
5+
"LocationFilter": {
6+
"LocationDepth": {
7+
"Value": [1],
8+
"Operator": "eq"
9+
}
10+
},
11+
"limit": 10,
12+
"offset": 0
13+
}
14+
}
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ViewInput>
3+
<identifier>TitleView</identifier>
4+
<Query>
5+
<LocationFilter>
6+
<LocationDepth>
7+
<Operator>eq</Operator>
8+
<Value>
9+
<Value>1</Value>
10+
</Value>
11+
</LocationDepth>
12+
</LocationFilter>
13+
<limit>10</limit>
14+
<offset>0</offset>
15+
</Query>
16+
</ViewInput>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"ViewInput": {
3+
"identifier": "TitleView",
4+
"Query": {
5+
"LocationFilter": {
6+
"LocationDepth": {
7+
"Value": [1, 2],
8+
"Operator": "in"
9+
}
10+
},
11+
"limit": 10,
12+
"offset": 0
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)