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

EZP-30027: [REST] Fixed logical op. parsing for Criterions of the same type #2527

Merged
merged 2 commits into from
May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions eZ/Bundle/EzPublishRestBundle/Tests/Functional/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,16 @@ private static function clearCreatedElement(array $contentArray)
}

/**
* @param string $string The value of the folders name field
* @param string $parentLocationId The REST id of the parent location
* @param null $remoteId
*
* @return array created Content, as an array
*/
protected function createFolder($string, $parentLocationId)
protected function createFolder($string, $parentLocationId, $remoteId = null)
{
$string = $this->addTestSuffix($string);
$remoteId = md5(uniqid($string, true));
$remoteId = $remoteId ?: md5(uniqid($string, true));
$xml = <<< XML
<?xml version="1.0" encoding="UTF-8"?>
<ContentCreate>
Expand Down
83 changes: 83 additions & 0 deletions eZ/Bundle/EzPublishRestBundle/Tests/Functional/ViewTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/**
* File containing the Functional\ViewTest class.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Bundle\EzPublishRestBundle\Tests\Functional;

class ViewTest extends TestCase
{
/**
* Covers POST /views.
*/
public function testViewRequestWithOrStatement()
{
$fooRemoteId = md5('View test content foo');
$barRemoteId = md5('View test content bar');
$this->createFolder('View test content foo', '/api/ezp/v2/content/locations/1/2', $fooRemoteId);
$this->createFolder('View test content bar', '/api/ezp/v2/content/locations/1/2', $barRemoteId);

$request = $this->createHttpRequest('POST', '/api/ezp/v2/views', 'ViewInput+xml', 'View+json');
$body = <<< XML
<?xml version="1.0" encoding="UTF-8"?>
<ViewInput>
<identifier>TitleView</identifier>
<Query>
<Filter>
<OR>
<ContentRemoteIdCriterion>{$fooRemoteId}</ContentRemoteIdCriterion>
<ContentRemoteIdCriterion>{$barRemoteId}</ContentRemoteIdCriterion>
</OR>
</Filter>
<limit>10</limit>
<offset>0</offset>
</Query>
</ViewInput>
XML;
$request->setContent($body);
$response = $this->sendHttpRequest($request);
$responseData = json_decode($response->getContent(), true);

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

/**
* Covers POST /views.
*
* @depends testViewRequestWithOrStatement
*/
public function testViewRequestWithAndStatement()
{
$fooRemoteId = md5('View test content foo');
$barRemoteId = md5('View test content bar');

$request = $this->createHttpRequest('POST', '/api/ezp/v2/views', 'ViewInput+xml', 'View+json');
$body = <<< XML
<?xml version="1.0" encoding="UTF-8"?>
<ViewInput>
<identifier>TitleView</identifier>
<Query>
<Filter>
<AND>
<OR>
<ContentRemoteIdCriterion>{$fooRemoteId}</ContentRemoteIdCriterion>
<ContentRemoteIdCriterion>{$barRemoteId}</ContentRemoteIdCriterion>
</OR>
<ContentRemoteIdCriterion>{$fooRemoteId}</ContentRemoteIdCriterion>
</AND>
</Filter>
<limit>10</limit>
<offset>0</offset>
</Query>
</ViewInput>
XML;
$request->setContent($body);
$response = $this->sendHttpRequest($request);
$responseData = json_decode($response->getContent(), true);

self::assertEquals(1, $responseData['View']['Result']['count']);
}
}
26 changes: 18 additions & 8 deletions eZ/Publish/Core/REST/Server/Input/Parser/Criterion/LogicalAnd.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@
*/
namespace eZ\Publish\Core\REST\Server\Input\Parser\Criterion;

use eZ\Publish\Core\REST\Server\Input\Parser\Criterion as CriterionParser;
use eZ\Publish\Core\REST\Common\Input\ParsingDispatcher;
use eZ\Publish\Core\REST\Common\Exceptions;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as LogicalAndCriterion;
use eZ\Publish\API\Repository\Values;

/**
* Parser for LogicalAnd Criterion.
*/
class LogicalAnd extends CriterionParser
class LogicalAnd extends LogicalOperator
{
/**
* @var string
*/
const TAG_NAME = 'AND';

/**
* Parses input structure to a LogicalAnd Criterion object.
*
Expand All @@ -30,15 +34,21 @@ class LogicalAnd extends CriterionParser
*/
public function parse(array $data, ParsingDispatcher $parsingDispatcher)
{
if (!array_key_exists('AND', $data) && !is_array($data['AND'])) {
throw new Exceptions\Parser('Invalid <AND> format');
if (!array_key_exists(static::TAG_NAME, $data) || !is_array($data[static::TAG_NAME])) {
throw new Exceptions\Parser('Invalid <' . static::TAG_NAME . '> format');
}

$criteria = array();
foreach ($data['AND'] as $criterionName => $criterionData) {
$criteria[] = $this->dispatchCriterion($criterionName, $criterionData, $parsingDispatcher);

$flattenedCriteriaElements = $this->getFlattenedCriteriaData($data[static::TAG_NAME]);
foreach ($flattenedCriteriaElements as $criterionElement) {
$criteria[] = $this->dispatchCriterion(
$criterionElement['type'],
$criterionElement['data'],
$parsingDispatcher
);
}

return new LogicalAndCriterion($criteria);
return new Values\Content\Query\Criterion\LogicalAnd($criteria);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
*/
namespace eZ\Publish\Core\REST\Server\Input\Parser\Criterion;

use eZ\Publish\Core\REST\Common\Input\BaseParser;
use eZ\Publish\Core\REST\Common\Input\ParsingDispatcher;
use eZ\Publish\Core\REST\Server\Input\Parser\Criterion;

/**
* Parser for LogicalOperator Criterion.
*/
class LogicalOperator extends BaseParser
class LogicalOperator extends Criterion
{
/**
* Parses input structure to a Criterion object.
Expand All @@ -30,4 +30,41 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher)
{
throw new \Exception('@todo implement');
}

/**
* @param array $criteriaByType
* @return array
*/
protected function getFlattenedCriteriaData(array $criteriaByType)
{
$criteria = [];
foreach ($criteriaByType as $type => $criterion) {
if (!is_array($criterion) || !$this->isZeroBasedArray($criterion)) {
$criterion = [$criterion];
}

foreach ($criterion as $criterionElement) {
$criteria[] = [
'type' => $type,
'data' => $criterionElement,
];
}
}

return $criteria;
}

/**
* Checks if the given $value is zero based.
*
* @param array $value
*
* @return bool
*/
protected function isZeroBasedArray(array $value)
{
reset($value);

return empty($value) || key($value) === 0;
}
}
28 changes: 19 additions & 9 deletions eZ/Publish/Core/REST/Server/Input/Parser/Criterion/LogicalOr.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@
*/
namespace eZ\Publish\Core\REST\Server\Input\Parser\Criterion;

use eZ\Publish\Core\REST\Server\Input\Parser\Criterion as CriterionParser;
use eZ\Publish\Core\REST\Common\Input\ParsingDispatcher;
use eZ\Publish\Core\REST\Common\Exceptions;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOr as LogicalOrCriterion;
use eZ\Publish\API\Repository\Values;

/**
* Parser for LogicalOr Criterion.
*/
class LogicalOr extends CriterionParser
class LogicalOr extends LogicalOperator
{
/**
* Parses input structure to a LogicalAnd Criterion object.
* @var string
*/
const TAG_NAME = 'OR';

/**
* Parses input structure to a LogicalOr Criterion object.
*
* @param array $data
* @param \eZ\Publish\Core\REST\Common\Input\ParsingDispatcher $parsingDispatcher
Expand All @@ -30,15 +34,21 @@ class LogicalOr extends CriterionParser
*/
public function parse(array $data, ParsingDispatcher $parsingDispatcher)
{
if (!array_key_exists('OR', $data) && !is_array($data['OR'])) {
throw new Exceptions\Parser('Invalid <OR> format');
if (!array_key_exists(static::TAG_NAME, $data) || !is_array($data[static::TAG_NAME])) {
throw new Exceptions\Parser('Invalid <' . static::TAG_NAME . '> format');
}

$criteria = array();
foreach ($data['OR'] as $criterionName => $criterionData) {
$criteria[] = $this->dispatchCriterion($criterionName, $criterionData, $parsingDispatcher);

$flattenedCriteriaElements = $this->getFlattenedCriteriaData($data[static::TAG_NAME]);
foreach ($flattenedCriteriaElements as $criterionElement) {
$criteria[] = $this->dispatchCriterion(
$criterionElement['type'],
$criterionElement['data'],
$parsingDispatcher
);
}

return new LogicalOrCriterion($criteria);
return new Values\Content\Query\Criterion\LogicalOr($criteria);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

/**
* File containing a test class.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Publish\Core\REST\Server\Tests\Input\Parser\Criterion;

use eZ\Publish\API\Repository\Values\Content;
use eZ\Publish\Core\REST\Common\Input\ParsingDispatcher;
use eZ\Publish\Core\REST\Server\Input\Parser;
use eZ\Publish\Core\REST\Server\Tests\Input\Parser\BaseTest;

class LogicalAndTest extends BaseTest
{
/**
* Logical parsing of AND statement.
*
* Notice regarding multiple criteria of same type:
*
* The XML decoder of EZ is not creating numeric arrays, instead using the tag as the array key. See
* variable $logicalAndParsedFromXml. This causes the ContentTypeIdentifierCriterion-Tag to appear as one-element
* (type numeric array) and two criteria configuration inside. The logical or parser will take care
* of this and return a flatt LogicalAnd criterion with 3 criteria inside.
*
* ```
* <AND>
* <ContentTypeIdentifierCriterion>author</ContentTypeIdentifierCriterion>
* <ContentTypeIdentifierCriterion>book</ContentTypeIdentifierCriterion>
* <Field>
* <name>title</name>
* <operator>EQ</operator>
* <value>Contributing to projects</value>
* </Field>
* </AND>
* ```
*/
public function testParseLogicalAnd()
{
$logicalAndParsedFromXml = [
'AND' => [
'ContentTypeIdentifierCriterion' => [
0 => 'author',
1 => 'book',
],
'Field' => [
'name' => 'title',
'operator' => 'EQ',
'value' => 'Contributing to projects',
],
],
];

$criterionMock = $this->getMock(Content\Query\Criterion::class, [], [], '', false);

$parserMock = $this->getMock(\eZ\Publish\Core\REST\Common\Input\Parser::class);
$parserMock->method('parse')->willReturn($criterionMock);

$result = $this->internalGetParser()->parse($logicalAndParsedFromXml, new ParsingDispatcher([
'application/vnd.ez.api.internal.criterion.ContentTypeIdentifier' => $parserMock,
'application/vnd.ez.api.internal.criterion.Field' => $parserMock,
]));

self::assertInstanceOf(Content\Query\Criterion\LogicalAnd::class, $result);
self::assertCount(3, (array)$result->criteria);
}

/**
* @expectedException \eZ\Publish\Core\REST\Common\Exceptions\Parser
*/
public function testThrowsExceptionOnInvalidAndStatement()
{
$this->internalGetParser()->parse(['AND' => 'Should be an array'], new ParsingDispatcher());
}

/**
* @return Parser\Criterion\LogicalAnd
*/
protected function internalGetParser()
{
return new Parser\Criterion\LogicalAnd();
}
}
Loading