Skip to content

Commit

Permalink
[BUGFIX] Use correct range in pagination when exceeding max
Browse files Browse the repository at this point in the history
When a range is passed via query string parameter, the correct
pagination links should be calculated. Even if the passed range
exceeds the maximum allowed range.
  • Loading branch information
Mateu Aguiló Bosch committed Feb 15, 2016
1 parent 162251e commit 6b80e32
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 85 deletions.
31 changes: 27 additions & 4 deletions src/Plugin/formatter/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace Drupal\restful\Plugin\formatter;

use Doctrine\Common\Collections\ArrayCollection;
use Drupal\Component\Plugin\PluginBase;
use Drupal\restful\Exception\ServerConfigurationException;
use Drupal\restful\Plugin\ConfigurablePluginTrait;
Expand All @@ -17,8 +16,12 @@
use Drupal\restful\Plugin\resource\ResourceInterface;
use Drupal\restful\RenderCache\Entity\CacheFragment;
use Drupal\restful\RenderCache\RenderCache;
use Drupal\restful\RenderCache\RenderCacheInterface;

/**
* Class Formatter.
*
* @package Drupal\restful\Plugin\formatter
*/
abstract class Formatter extends PluginBase implements FormatterInterface {

use ConfigurablePluginTrait;
Expand Down Expand Up @@ -207,7 +210,8 @@ protected function setCachedData($data, $output, array $parent_hashes = array())
* @param mixed $data
* The data to be rendered.
*
* @return RenderCacheInterface
* @return \Drupal\restful\RenderCache\RenderCacheInterface;
* The cache controller.
*/
protected function createCacheController($data) {
Expand All @@ -234,7 +238,8 @@ protected function createCacheController($data) {
* @param mixed $data
* The data to be rendered.
*
* @return ArrayCollection
* @return \Doctrine\Common\Collections\ArrayCollection;
* The cache controller.
*/
protected static function cacheFragments($data) {
Expand Down Expand Up @@ -309,4 +314,22 @@ protected static function unprefixInputOptions($allowed_fields, $prefix) {
return array_filter(array_map($closure_unprefix, $allowed_fields));
}


/**
* Helper function that calculates the number of items per page.
*
* @param ResourceInterface $resource
* The associated resource.
*
* @return int
* The items per page.
*/
protected function calculateItemsPerPage(ResourceInterface $resource) {
$data_provider = $resource->getDataProvider();
$max_range = $data_provider->getRange();
$original_input = $resource->getRequest()->getParsedInput();
$items_per_page = empty($original_input['range']) ? $max_range : $original_input['range'];
return $items_per_page > $max_range ? $max_range : $items_per_page;
}

}
3 changes: 1 addition & 2 deletions src/Plugin/formatter/FormatterHalJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ protected function addHateoas(array &$data) {
);

$input = $request->getParsedInput();
$data_provider = $resource->getDataProvider();
$page = !empty($input['page']) ? $input['page'] : 1;

if ($page > 1) {
Expand All @@ -138,7 +137,7 @@ protected function addHateoas(array &$data) {
// We know that there are more pages if the total count is bigger than the
// number of items of the current request plus the number of items in
// previous pages.
$items_per_page = $data_provider->getRange();
$items_per_page = $this->calculateItemsPerPage($resource);
$previous_items = ($page - 1) * $items_per_page;
if (isset($data['count']) && $data['count'] > $listed_items + $previous_items) {
$input['page'] = $page + 1;
Expand Down
2 changes: 1 addition & 1 deletion src/Plugin/formatter/FormatterJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ protected function addHateoas(array &$data) {
// We know that there are more pages if the total count is bigger than the
// number of items of the current request plus the number of items in
// previous pages.
$items_per_page = $resource->getDataProvider()->getRange();
$items_per_page = $this->calculateItemsPerPage($resource);
$previous_items = ($page - 1) * $items_per_page;
if (isset($data['count']) && $data['count'] > count($data['data']) + $previous_items) {
$query = array('page' => $page + 1) + $input;
Expand Down
3 changes: 1 addition & 2 deletions src/Plugin/formatter/FormatterJsonApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,12 @@ protected function addHateoas(array &$data, ResourceInterface $resource = NULL,
$options = $top_level ? array('query' => $input) : array();
$data['links']['self'] = $resource->versionedUrl($path, $options);

$data_provider = $resource->getDataProvider();
$page = !empty($input['page']) ? $input['page'] : 1;

// We know that there are more pages if the total count is bigger than the
// number of items of the current request plus the number of items in
// previous pages.
$items_per_page = empty($original_input['range']) ? $data_provider->getRange() : $original_input['range'];
$items_per_page = $this->calculateItemsPerPage($resource);
if (isset($data['meta']['count']) && $data['meta']['count'] > $items_per_page) {
$num_pages = ceil($data['meta']['count'] / $items_per_page);
unset($input['page']);
Expand Down
131 changes: 66 additions & 65 deletions tests/RestfulJsonApiTestCase.test
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,41 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
$this->formatter->setConfiguration($configuration);
}

/**
* Create test entities.
*
* @return Entity[]
* An array of entities.
*/
protected function createEntities() {
$entities = array();
$entities[] = entity_create('entity_test', array(
'name' => 'main',
'uid' => $this->account->uid,
));
$entities[0]->save();

$entities[] = entity_create('entity_test', array(
'name' => 'main',
'uid' => $this->account->uid,
));
$entities[1]->save();

$entities[] = entity_create('entity_test', array(
'name' => 'main',
'uid' => $this->account->uid,
));

$entities[0]->entity_reference_single[LANGUAGE_NONE][] = array('target_id' => $entities[1]->pid);
$entities[0]->save();
$entities[2]->entity_reference_single[LANGUAGE_NONE][] = array('target_id' => $entities[0]->pid);
$entities[2]->entity_reference_multiple[LANGUAGE_NONE][] = array('target_id' => $entities[0]->pid);
$entities[2]->entity_reference_multiple[LANGUAGE_NONE][] = array('target_id' => $entities[1]->pid);
$entities[2]->save();

return $entities;
}

/**
* Test requesting resources.
*/
Expand Down Expand Up @@ -130,7 +165,7 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
// embedded entity if there is no subfield added.
$this->handler->setRequest(Request::create('api/v1.1/main/' . $wrapper->getIdentifier(), array(
'include' => 'entity_reference_multiple_resource,entity_reference_single_resource',
'fields' => 'entity_reference_multiple_resource,entity_reference_single_resource'
'fields' => 'entity_reference_multiple_resource,entity_reference_single_resource',
)));
$this->handler->setPath($wrapper->getIdentifier());
$this->formatter->setResource($this->handler);
Expand All @@ -141,7 +176,7 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
// Now make sure that including fields only lists those fields.
$this->handler->setRequest(Request::create('api/v1.1/main/' . $wrapper->getIdentifier(), array(
'include' => 'entity_reference_single_resource',
'fields' => 'entity_reference_single_resource.label'
'fields' => 'entity_reference_single_resource.label',
)));
$this->handler->setPath($wrapper->getIdentifier());
$this->formatter->setResource($this->handler);
Expand Down Expand Up @@ -401,7 +436,10 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
);
drupal_write_record('restful_test_db_query', $record);
$handler = restful()->getResourceManager()->getPlugin('main:1.8');
$handler->setRequest(Request::create('', array('include' => 'random_rel', 'range' => 1)));
$handler->setRequest(Request::create('', array(
'include' => 'random_rel',
'range' => 1,
)));
$handler->setPath(1);
$formatter = restful()->getFormatterManager()->getPlugin('json_api');
$formatter->setResource($handler);
Expand Down Expand Up @@ -437,14 +475,14 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
'last' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 5
)
'page' => 5,
),
)),
'next' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 2
)
'page' => 2,
),
)),
);
$this->assertEqual($links, $expected_links);
Expand All @@ -453,7 +491,7 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
$range = 2;
$this->handler->setRequest(Request::create('', array(
'range' => $range,
'page' => 2
'page' => 2,
)));
$this->handler->setPath('');
$this->formatter->setResource($this->handler);
Expand All @@ -464,27 +502,27 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
'self' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 2
)
'page' => 2,
),
)),
'first' => $this->handler->versionedUrl('', array('query' => array('range' => $range))),
'previous' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 1
)
'page' => 1,
),
)),
'last' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 5
)
'page' => 5,
),
)),
'next' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 3
)
'page' => 3,
),
)),
);
$this->assertEqual($links, $expected_links);
Expand All @@ -493,7 +531,7 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
$range = 2;
$this->handler->setRequest(Request::create('', array(
'range' => $range,
'page' => 5
'page' => 5,
)));
$this->handler->setPath('');
$this->formatter->setResource($this->handler);
Expand All @@ -504,21 +542,21 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
'self' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 5
)
'page' => 5,
),
)),
'first' => $this->handler->versionedUrl('', array('query' => array('range' => $range))),
'previous' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 4
)
'page' => 4,
),
)),
'last' => $this->handler->versionedUrl('', array(
'query' => array(
'range' => $range,
'page' => 5
)
'page' => 5,
),
)),
);
$this->assertEqual($links, $expected_links);
Expand All @@ -537,8 +575,7 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {

// Add files and taxonomy term references.
$query = new EntityFieldQuery();
$result = $query
->entityCondition('entity_type', 'taxonomy_term')
$result = $query->entityCondition('entity_type', 'taxonomy_term')
->entityCondition('bundle', 'test_vocab')
->execute();
$tids = array_keys($result['taxonomy_term']);
Expand All @@ -554,15 +591,15 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
$entities[2]->term_multiple[LANGUAGE_NONE][] = array('tid' => $tids[2]);
$entities[2]->file_single[LANGUAGE_NONE][] = array(
'fid' => $images[0],
'display' => TRUE
'display' => TRUE,
);
$entities[2]->file_multiple[LANGUAGE_NONE][] = array(
'fid' => $images[1],
'display' => TRUE
'display' => TRUE,
);
$entities[2]->file_multiple[LANGUAGE_NONE][] = array(
'fid' => $images[2],
'display' => TRUE
'display' => TRUE,
);
entity_save('entity_test', $entities[2]);

Expand Down Expand Up @@ -600,8 +637,7 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
)));
$handler->setPath($entities[2]->uuid);
$format_manager->setResource($handler);
$response = $format_manager
->negotiateFormatter(NULL, 'json_api')
$response = $format_manager->negotiateFormatter(NULL, 'json_api')
->prepare($handler->process());

$result = $response['data']['attributes'];
Expand Down Expand Up @@ -765,39 +801,4 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
$this->assertEqual($expected, $parsed_body);
}

/**
* Create test entities.
*
* @return Entity[]
* An array of entities.
*/
protected function createEntities() {
$entities = array();
$entities[] = entity_create('entity_test', array(
'name' => 'main',
'uid' => $this->account->uid,
));
$entities[0]->save();

$entities[] = entity_create('entity_test', array(
'name' => 'main',
'uid' => $this->account->uid,
));
$entities[1]->save();

$entities[] = entity_create('entity_test', array(
'name' => 'main',
'uid' => $this->account->uid,
));

$entities[0]->entity_reference_single[LANGUAGE_NONE][] = array('target_id' => $entities[1]->pid);
$entities[0]->save();
$entities[2]->entity_reference_single[LANGUAGE_NONE][] = array('target_id' => $entities[0]->pid);
$entities[2]->entity_reference_multiple[LANGUAGE_NONE][] = array('target_id' => $entities[0]->pid);
$entities[2]->entity_reference_multiple[LANGUAGE_NONE][] = array('target_id' => $entities[1]->pid);
$entities[2]->save();

return $entities;
}

}
Loading

0 comments on commit 6b80e32

Please sign in to comment.