Skip to content

Commit

Permalink
Added support for limit, pagination, and changed the request and resp…
Browse files Browse the repository at this point in the history
…onse structure to a more standarized way. Also. created a base sync repository that can be extended
  • Loading branch information
bvisonl committed Jul 25, 2018
1 parent ec1b7a5 commit ed99513
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 29 deletions.
6 changes: 4 additions & 2 deletions Controller/SyncController.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ public function pullAction(Request $request) {
$mappings = (isset($data["mappings"])) ? $data["mappings"] : array();
}

$changes = $this->get('nti.sync')->getFromMappings($mappings);
$resultData = $this->get('nti.sync')->getFromMappings($mappings);

return new JsonResponse($changes, 200);
$resultData = json_decode($this->container->get('jms_serializer')->serialize($resultData, 'json'), true);

return new JsonResponse($resultData, 200);
}

/**
Expand Down
19 changes: 8 additions & 11 deletions Interfaces/SyncRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,29 @@

namespace NTI\SyncBundle\Interfaces;

use NTI\SyncBundle\Models\SyncPullRequestData;
use NTI\SyncBundle\Models\SyncPullResponseData;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Interface SyncRepositoryInterface
* @package NTI\SyncBundle\Interfaces
*/
interface SyncRepositoryInterface {

/**
* This function should return a plain array containing the results to be sent to the client
* This function should return an instance of SyncPullResponseData containing the results to be sent to the client
* when a sync is requested. The container is also passed as a parameter in order to give additional
* flexibility to the repository when making decision on what to show to the client. For example, if the user
* making the request only has access to a portion of the data, this can be handled via the container in this method
* of the repository.
*
* The resulting structure should be the following:
*
* array(
* "data" => (array of objects),
* SyncState::REAL_LAST_TIMESTAMP => (last updated_on date from the array of objects),
* )
* The resulting structure should be an instance of SyncPullRequestData
*
*
* @param $timestamp
* @param ContainerInterface $container
* @param array $serializationGroups
* @return mixed
* @param SyncPullRequestData $requestData
* @return SyncPullResponseData
*/
public function findFromTimestamp($timestamp, ContainerInterface $container, $serializationGroups = array());
public function findFromTimestamp(ContainerInterface $container, SyncPullRequestData $requestData);
}
153 changes: 153 additions & 0 deletions Models/SyncPullRequestData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

namespace NTI\SyncBundle\Models;

use JMS\Serializer\Annotation as JMS;

/**
* Class SyncPullRequestData
* @package NTI\SyncBundle\Models
*/
class SyncPullRequestData {

/**
* @var string
* @JMS\Type("string")
*
* The name of the SyncMapping
*/
private $mapping;

/**
* @var int
* @JMS\Type("integer")
*
* The timestamp that the client wants to pull from
*/
private $timestamp;

/**
* @var int
* @JMS\Type("integer")
*
* The amount of results that should be returned
*/
private $limit;

/**
* @var int
* @JMS\Type("integer")
*
* The page for the current timestamp.
*
* Explanation: Let's say that the server is going to send results in batch of 50s
* If there are 300 results whose last_timestamp is > than the timestamp provided
* and all of those 300 results have the same timestamp (for example, it is normal to
* set the inital timestamp to 0 when first installing this bundle) this would cause
* a loop and the client would always sync the same 50 results over and over again.
*
* For this, the client can send the `page` parameter, which then can be used in the repository to offset the results.
*
*/
private $page = 1;

/**
* @var array|string
* @JMS\Type("array")
*
* The serialization groups that the process should use when returning the results
*/
private $serializationGroups = array("sync_basic");

/**
* @return string
*/
public function getMapping()
{
return $this->mapping;
}

/**
* @param string $mapping
* @return SyncPullRequestData
*/
public function setMapping($mapping)
{
$this->mapping = $mapping;
return $this;
}

/**
* @return int
*/
public function getTimestamp()
{
return $this->timestamp;
}

/**
* @param int $timestamp
* @return SyncPullRequestData
*/
public function setTimestamp($timestamp)
{
$this->timestamp = $timestamp;
return $this;
}

/**
* @return int
*/
public function getLimit()
{
return $this->limit;
}

/**
* @param int $limit
* @return SyncPullRequestData
*/
public function setLimit($limit)
{
$this->limit = $limit;
return $this;
}

/**
* @return int
*/
public function getPage()
{
return $this->page;
}

/**
* @param int $page
* @return SyncPullRequestData
*/
public function setPage($page)
{
$this->page = $page;
return $this;
}

/**
* @return array|string
*/
public function getSerializationGroups()
{
return $this->serializationGroups;
}

/**
* @param array|string $serializationGroups
* @return SyncPullRequestData
*/
public function setSerializationGroups($serializationGroups)
{
$this->serializationGroups = $serializationGroups;
return $this;
}


}
94 changes: 94 additions & 0 deletions Models/SyncPullResponseData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace NTI\SyncBundle\Models;

use JMS\Serializer\Annotation as JMS;

/**
* Class SyncPullResponseData
* @package NTI\SyncBundle\Models
*
*/
class SyncPullResponseData {

/**
* @var int
* @JMS\SerializedName("real_last_timestamp")
*
* This is the last timestamp of the batch of results provided. Basically, it's the timestamp of the last element
* in the array of provided elements in data. This is useful for the client so it can continue synching from this
* timestamp.
*/
private $realLastTimestamp;

/**
* @var array
*
* The list of elements that changed.
*/
private $data;

/**
* @var int
* @JMS\SerializedName("total_count")
*
* This is the total count of results from the timestamp provided. Useful for the client to know if there are more results and how many more.
*/
private $totalCount;

/**
* @return int
*/
public function getRealLastTimestamp(): int
{
return $this->realLastTimestamp;
}

/**
* @param int $realLastTimestamp
* @return SyncPullResponseData
*/
public function setRealLastTimestamp(int $realLastTimestamp): SyncPullResponseData
{
$this->realLastTimestamp = $realLastTimestamp;
return $this;
}

/**
* @return array
*/
public function getData(): array
{
return $this->data;
}

/**
* @param array $data
* @return SyncPullResponseData
*/
public function setData(array $data): SyncPullResponseData
{
$this->data = $data;
return $this;
}

/**
* @return int
*/
public function getTotalCount(): int
{
return $this->totalCount;
}

/**
* @param int $totalCount
* @return SyncPullResponseData
*/
public function setTotalCount(int $totalCount): SyncPullResponseData
{
$this->totalCount = $totalCount;
return $this;
}


}
64 changes: 64 additions & 0 deletions Repository/SyncRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace NTI\SyncBundle\Repository;

use Doctrine\ORM\EntityRepository;
use JMS\Serializer\SerializationContext;
use NTI\SyncBundle\Models\SyncPullRequestData;
use NTI\SyncBundle\Models\SyncPullResponseData;
use NTI\SyncBundle\Entity\SyncState;
use NTI\SyncBundle\Interfaces\SyncRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class SyncRepository extends EntityRepository implements SyncRepositoryInterface {

/**
* @inheritdoc
*/
public function findFromTimestamp(ContainerInterface $container, SyncPullRequestData $requestData)
{
$timestamp = $requestData->getTimestamp();
$serializationGroups = $requestData->getSerializationGroups();
$page = $requestData->getPage() > 0 ? $requestData->getPage() - 1 : 0;
$limit = $requestData->getLimit();

// Joins
$qb = $this->createQueryBuilder('i');
$qb->andWhere($qb->expr()->gte('i.lastTimestamp', $timestamp));
$qb->orderBy('i.lastTimestamp', 'asc');

/**
* This should be set BEFORE getting the total count, that way the client will receive
* the actual items that are left for it to sync, not the total amount from a timestamp.
* @Ref the "page"parameter in SyncPulLRequestData
*/
$qb->setFirstResult($page * $limit);

// Total records
$totalCountQb = clone $qb;
$totalCountQb->select('COUNT(i.id)');
$totalCountQuery = $totalCountQb->getQuery();

try {
$totalCount = intval($totalCountQuery->getSingleScalarResult());
} catch (\Exception $e) {
$totalCount = 0;
}

$qb->setMaxResults($limit);

$items = $qb->getQuery()->getResult();

$realLastTimestamp = count($items) <= 0 ? $timestamp : $items[count($items) - 1]->getLastTimestamp();

$itemsArray = json_decode($container->get('jms_serializer')->serialize($items, 'json', SerializationContext::create()->setGroups($serializationGroups)), true);

$result = new SyncPullResponseData();
$result->setData($itemsArray);
$result->setRealLastTimestamp($realLastTimestamp);
$result->setTotalCount($totalCount);

return $result;
}

}
Loading

0 comments on commit ed99513

Please sign in to comment.