Commit 2dcc1b00 authored by Emiliano Balbuena's avatar Emiliano Balbuena

(wip): Feed endpoint redux

No related merge requests found
Pipeline #101806358 failed with stages
in 2 minutes and 56 seconds
......@@ -24,7 +24,10 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
/** @var bool */
protected $lastPage = false;
public function __construct(array $data = null, $pagingToken = null)
/** @var array */
protected $attributes = [];
public function __construct(array $data = null, $pagingToken = null, array $attributes = [])
{
if ($data !== null) {
$this->data = $data;
......@@ -33,6 +36,10 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
if ($pagingToken !== null) {
$this->pagingToken = $pagingToken;
}
if ($this->attributes) {
$this->attributes = [];
}
}
/**
......@@ -96,7 +103,7 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
}
/**
* Returns if the result set is fauly
* Returns if the result set is faulty
* @return bool
*/
public function hasFailed()
......@@ -104,6 +111,44 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
return !!$this->exception;
}
/**
* @param array $attributes
* @return Response
*/
public function setAttributes(array $attributes)
{
$this->attributes = $attributes;
return $this;
}
/**
* @return array
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* @param string $key
* @param mixed $value
* @return Response
*/
public function setAttribute(string $key, $value)
{
$this->attributes[$key] = $value;
return $this;
}
/**
* @param string $key
* @return mixed|null
*/
public function getAttribute(string $key)
{
return $this->attributes[$key] ?? null;
}
/**
* Return the current element
* @link http://php.net/manual/en/iterator.current.php
......@@ -296,7 +341,7 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
*/
public function reverse($preserveKeys = false)
{
return new static(array_reverse($this->data, $preserveKeys), $this->pagingToken);
return new static(array_reverse($this->data, $preserveKeys), $this->pagingToken, $this->attributes);
}
/**
......@@ -315,7 +360,7 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
$filtered = array_values($filtered);
}
return new static($filtered, $this->pagingToken);
return new static($filtered, $this->pagingToken, $this->attributes);
}
/**
......@@ -325,7 +370,7 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
*/
public function map($callback)
{
return new static(array_map($callback, $this->data), $this->pagingToken);
return new static(array_map($callback, $this->data), $this->pagingToken, $this->attributes);
}
/**
......
......@@ -29,104 +29,157 @@ class feeds implements Interfaces\Api
{
Factory::isLoggedIn();
$now = time();
$periodsInSecs = Core\Feeds\Elastic\Repository::PERIODS;
/** @var User $currentUser */
$currentUser = Core\Session::getLoggedinUser();
$filter = $pages[0] ?? null;
if (!$filter) {
return Factory::response([
'status' => 'error',
'message' => 'Invalid filter'
]);
}
/** @var User $actor */
$actor = Core\Session::getLoggedinUser();
$algorithm = $pages[1] ?? null;
if (!$algorithm) {
return Factory::response([
'status' => 'error',
'message' => 'Invalid algorithm'
]);
}
$type = '';
switch ($pages[2]) {
switch ($pages[2] ?? '') {
case 'activities':
$type = 'activity';
break;
case 'channels':
$type = 'user';
break;
case 'images':
$type = 'object:image';
break;
case 'videos':
$type = 'object:video';
break;
case 'groups':
$type = 'group';
break;
case 'blogs':
$type = 'object:blog';
break;
}
$period = $_GET['period'] ?? '12h';
if ($algorithm === 'hot') {
$period = '12h';
} elseif ($algorithm === 'latest') {
$period = '1y';
}
//
$hardLimit = 600;
if ($currentUser && $currentUser->isAdmin()) {
$hardLimit = 5000;
default:
$type = $pages[2] ?? '';
}
$offset = 0;
$limit = ($_GET['limit'] ?? 0) ?: 12;
if (isset($_GET['offset'])) {
$offset = intval($_GET['offset']);
}
$hashtags =
$limit = 12;
$feedCollection = new Core\Feeds\FeedCollection();
$feedCollection
->setActor($actor)
->setFilter($pages[0] ?? '')
->setAlgorithm($pages[1] ?? '')
->setType($type)
->setPeriod($_GET['period'] ?? '12h')
->setLimit($limit)
->setOffset($_GET['offset'] ?? 0)
->setCap($actor->isAdmin() ? 5000 : 600)
->setAll((bool) $_GET['all'] ?? false)
->setHashtags($hashtags)
;
if (isset($_GET['limit'])) {
$limit = abs(intval($_GET['limit']));
}
/////////////////////////////////////////////////////////
if (($offset + $limit) > $hardLimit) {
$limit = $hardLimit - $offset;
}
if ($limit <= 0) {
return Factory::response([
'status' => 'success',
'entities' => [],
'load-next' => $hardLimit,
'overflow' => true,
]);
}
//
$hashtag = null;
if (isset($_GET['hashtag'])) {
$hashtag = strtolower($_GET['hashtag']);
}
$now = time();
$periodsInSecs = Core\Feeds\Elastic\Repository::PERIODS;
$all = false;
if (!$hashtag && isset($_GET['all']) && $_GET['all']) {
$all = true;
}
// /** @var User $currentUser */
// $currentUser = Core\Session::getLoggedinUser();
// $filter = $pages[0] ?? null;
//
// if (!$filter) {
// return Factory::response([
// 'status' => 'error',
// 'message' => 'Invalid filter'
// ]);
// }
// $algorithm = $pages[1] ?? null;
// if (!$algorithm) {
// return Factory::response([
// 'status' => 'error',
// 'message' => 'Invalid algorithm'
// ]);
// }
// $type = '';
//
// switch ($pages[2]) {
// case 'activities':
// $type = 'activity';
// break;
// case 'channels':
// $type = 'user';
// break;
// case 'images':
// $type = 'object:image';
// break;
// case 'videos':
// $type = 'object:video';
// break;
// case 'groups':
// $type = 'group';
// break;
// case 'blogs':
// $type = 'object:blog';
// break;
// }
// $period = $_GET['period'] ?? '12h';
//
// if ($algorithm === 'hot') {
// $period = '12h';
// } elseif ($algorithm === 'latest') {
// $period = '1y';
// }
// //
//
// $hardLimit = 600;
//
// if ($currentUser && $currentUser->isAdmin()) {
// $hardLimit = 5000;
// }
//
// $offset = 0;
//
// if (isset($_GET['offset'])) {
// $offset = intval($_GET['offset']);
// }
//
// $limit = 12;
//
// if (isset($_GET['limit'])) {
// $limit = abs(intval($_GET['limit']));
// }
// if (($offset + $limit) > $hardLimit) {
// $limit = $hardLimit - $offset;
// }
//
// if ($limit <= 0) {
// return Factory::response([
// 'status' => 'success',
// 'entities' => [],
// 'load-next' => $hardLimit,
// 'overflow' => true,
// ]);
// }
// //
//
// $hashtag = null;
// if (isset($_GET['hashtag'])) {
// $hashtag = strtolower($_GET['hashtag']);
// }
//
// $all = false;
// if (!$hashtag && isset($_GET['all']) && $_GET['all']) {
// $all = true;
// }
$sync = (bool) ($_GET['sync'] ?? false);
......
<?php
/**
* FeedCollection.
*
* @author edgebal
*/
namespace Minds\Core\Feeds;
use Exception;
use Minds\Common\Repository\Response;
use Minds\Entities\User;
class FeedCollection
{
/** @var User|null */
protected $actor = null;
/** @var string */
protected $filter;
/** @var string */
protected $algorithm;
/** @var string */
protected $type;
/** @var string */
protected $period;
/** @var int */
protected $limit = 12;
/** @var int */
protected $offset = 0;
/** @var int */
protected $cap = 600;
/** @var bool */
protected $all = true;
/** @var array|null */
protected $hashtags = null;
/**
* @param User|null $actor
* @return FeedCollection
*/
public function setActor(?User $actor): FeedCollection
{
$this->actor = $actor;
return $this;
}
/**
* @param string $filter
* @return FeedCollection
*/
public function setFilter(string $filter): FeedCollection
{
$this->filter = $filter;
return $this;
}
/**
* @param string $algorithm
* @return FeedCollection
*/
public function setAlgorithm(string $algorithm): FeedCollection
{
$this->algorithm = $algorithm;
return $this;
}
/**
* @param string $type
* @return FeedCollection
*/
public function setType(string $type): FeedCollection
{
$this->type = $type;
return $this;
}
/**
* @param string $period
* @return FeedCollection
*/
public function setPeriod(string $period): FeedCollection
{
$this->period = $period;
return $this;
}
/**
* @param int $limit
* @return FeedCollection
*/
public function setLimit(int $limit): FeedCollection
{
$this->limit = $limit;
return $this;
}
/**
* @param int $offset
* @return FeedCollection
*/
public function setOffset(int $offset): FeedCollection
{
$this->offset = $offset;
return $this;
}
/**
* @param int $cap
* @return FeedCollection
*/
public function setCap(int $cap): FeedCollection
{
$this->cap = $cap;
return $this;
}
/**
* @param bool $all
* @return FeedCollection
*/
public function setAll(bool $all): FeedCollection
{
$this->all = $all;
return $this;
}
/**
* @param array|null $hashtags
* @return FeedCollection
*/
public function setHashtags(?array $hashtags): FeedCollection
{
$this->hashtags = $hashtags;
return $this;
}
/**
* @throws Exception
*/
public function fetch()
{
if (!$this->filter) {
throw new Exception('Missing filter');
}
if (!$this->algorithm /* TODO: Validate */) {
throw new Exception('Missing algorithm');
}
if (!$this->type /* TODO: Validate */) {
throw new Exception('Missing type');
}
if (!$this->period /* TODO: Validate */) {
throw new Exception('Missing period');
}
$offset = abs(intval($this->offset ?: 0));
$limit = abs(intval($this->limit ?: 0));
if ($limit) {
if ($this->cap && ($offset + $limit) > $this->cap) {
$limit = $this->cap - $offset;
}
if ($limit < 0) {
$emptyResponse = new Response([]);
$emptyResponse
->setPagingToken((string) $this->cap)
->setLastPage(true)
->setAttribute('overflow', true);
return $emptyResponse;
}
}
$all = !$this->hashtags && $this->all;
$response = new Response();
return $response;
}
}
<?php
namespace Spec\Minds\Core\Feeds;
use Exception;
use Minds\Common\Repository\Response;
use Minds\Core\Feeds\FeedCollection;
use Minds\Entities\User;
use PhpSpec\Exception\Example\FailureException;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FeedCollectionSpec extends ObjectBehavior
{
public function it_is_initializable()
{
$this->shouldHaveType(FeedCollection::class);
}
public function it_should_fetch()
{
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->fetch()
->shouldBeAResponse([]);
}
public function it_should_fetch_with_acl_restrictions(
User $actor
) {
/** @var FeedCollection $this */
$this
->setActor($actor)
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->fetch()
->shouldBeAResponse([]);
}
public function it_should_fetch_using_offset_limit_and_cap()
{
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->setLimit(2)
->setOffset(0)
->setCap(3)
->fetch()
->shouldBeAResponse([]);
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->setLimit(2)
->setOffset(2)
->setCap(3)
->fetch()
->shouldBeAResponse([]);
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->setLimit(2)
->setOffset(4)
->setCap(3)
->fetch()
->shouldBeAResponse([], [ 'overflow' => true ]);
}
public function it_should_fetch_all_or_filtering_by_hashtag()
{
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->setAll(true)
->setHashtag('')
->fetch()
->shouldBeAResponse([]);
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->setAll(false)
->setHashtag('phpspec')
->fetch()
->shouldBeAResponse([]);
}
public function it_should_fetch_filtering_by_preferred_hashtags(
User $user
) {
/** @var FeedCollection $this */
$this
->setActor($user)
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->fetch()
->shouldBeAResponse([]);
}
public function it_should_throw_if_no_filter_during_fetch()
{
/** @var FeedCollection $this */
$this
->setFilter('')
->setAlgorithm('top')
->setType('activity')
->setPeriod('12h')
->shouldThrow(new Exception('Missing filter'))
->duringFetch();
}
public function it_should_throw_if_no_algorithm_during_fetch()
{
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('')
->setType('activity')
->setPeriod('12h')
->shouldThrow(new Exception('Missing algorithm'))
->duringFetch();
}
public function it_should_throw_if_no_type_during_fetch()
{
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('')
->setPeriod('12h')
->shouldThrow(new Exception('Missing type'))
->duringFetch();
}
public function it_should_throw_if_no_period_during_fetch()
{
/** @var FeedCollection $this */
$this
->setFilter('global')
->setAlgorithm('top')
->setType('activity')
->setPeriod('')
->shouldThrow(new Exception('Missing period'))
->duringFetch();
}
public function getMatchers(): array
{
$matchers = [];
$matchers['beAResponse'] = function ($subject, $elements = null, $attributes = null) {
if (!($subject instanceof Response)) {
throw new FailureException("Subject should be a Response");
}
if ($elements !== null && $elements !== $subject->toArray()) {
throw new FailureException("Subject elements don't match");
}
if ($attributes !== null && $attributes !== $subject->getAttributes()) {
throw new FailureException("Subject attributes don't match");
}
return true;
};
return $matchers;
}
}
Please register or to comment