Test implementation of subscriptions checking command.
This commit is contained in:
parent
032730d2b2
commit
5652a5ca68
|
@ -12,6 +12,11 @@ parameters:
|
|||
mailer_user: ~
|
||||
mailer_password: ~
|
||||
|
||||
point_base_url: https://point.im/
|
||||
point_api_base_url: https://point.im/api/
|
||||
point_use_https: true
|
||||
point_login: point-tools
|
||||
|
||||
locale: en
|
||||
|
||||
# A secret key that's used to generate certain security-related tokens
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Command;
|
||||
|
||||
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\SubscriptionsManager;
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\UserApi;
|
||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class UpdateSubscriptionsCommand extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('point:update:subscriptions')
|
||||
->setDescription('Update subscriptions of users subscribed to service')
|
||||
->addOption(
|
||||
'check-only',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'If set, command will not perform write operations in the database'
|
||||
)
|
||||
// @todo add option for checking only selected user
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var UserApi $api */
|
||||
$api = $this->getContainer()->get('skobkin_point_tools.api_user');
|
||||
/** @var SubscriptionsManager $subscriptionsManager */
|
||||
$subscriptionsManager = $this->getContainer()->get('skobkin_point_tools.subscriptions_manager');
|
||||
|
||||
$serviceUserName = $this->getContainer()->getParameter('point_login');
|
||||
$serviceUser = $this->getContainer()->get('doctrine.orm.entity_manager')->getRepository('SkobkinPointToolsBundle:User')->findOneBy(['login' => $serviceUserName]);
|
||||
|
||||
if (!$serviceUser) {
|
||||
// @todo Retrieving user
|
||||
}
|
||||
|
||||
$serviceSubscribers = $api->getUserSubscribersByLogin($serviceUserName);
|
||||
|
||||
// Updating service subscribers
|
||||
$subscriptionsManager->updateUserSubscribers($serviceUser, $serviceSubscribers);
|
||||
|
||||
// Updating service users subscribers
|
||||
foreach ($serviceSubscribers as $user) {
|
||||
$userCurrentSubscribers = $api->getUserSubscribersByLogin($user->getLogin());
|
||||
|
||||
$subscriptionsManager->updateUserSubscribers($user, $userCurrentSubscribers);
|
||||
|
||||
// @todo some pause for lower API load
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
* @ORM\Index(name="date_idx", columns={"date"})
|
||||
* })
|
||||
* @ORM\Entity
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
class SubscriptionEvent
|
||||
{
|
||||
|
@ -58,6 +59,17 @@ class SubscriptionEvent
|
|||
*/
|
||||
private $action;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\PrePersist
|
||||
*/
|
||||
public function onCreate()
|
||||
{
|
||||
if (!$this->date) {
|
||||
$this->date = new \DateTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
services:
|
||||
skobkin_point_tools.http_client:
|
||||
class: %guzzle.client.class%
|
||||
arguments: [ "http://point.im/" ]
|
||||
arguments: [ "%point_base_url%" ]
|
||||
tags:
|
||||
- { name: guzzle.client }
|
||||
|
||||
skobkin_point_tools.api_user:
|
||||
class: Skobkin\Bundle\PointToolsBundle\Service\UserApi
|
||||
arguments: [ @skobkin_point_tools.http_client ]
|
||||
arguments:
|
||||
- @skobkin_point_tools.http_client
|
||||
- "%point_use_https%"
|
||||
- "%point_api_base_url%"
|
||||
- @doctrine.orm.entity_manager
|
||||
|
||||
skobkin_point_tools.subscriptions_manager:
|
||||
class: Skobkin\Bundle\PointToolsBundle\Service\SubscriptionsManager
|
||||
arguments: [ @doctrine.orm.entity_manager ]
|
|
@ -6,7 +6,12 @@ use Guzzle\Service\Client;
|
|||
use Guzzle\Http\Message\Request as GuzzleRequest;
|
||||
use Guzzle\Http\Message\Response as GuzzleResponse;
|
||||
|
||||
// @todo Implement commands: https://github.com/misd-service-development/guzzle-bundle/blob/master/Resources/doc/serialization.md
|
||||
/**
|
||||
* @todo Implement commands
|
||||
* @see https://github.com/misd-service-development/guzzle-bundle/blob/master/Resources/doc/serialization.md
|
||||
* @see https://github.com/misd-service-development/guzzle-bundle/blob/master/Resources/doc/clients.md
|
||||
* @see https://github.com/misd-service-development/guzzle-bundle/blob/master/Resources/doc/param_converter.md
|
||||
*/
|
||||
class AbstractApi
|
||||
{
|
||||
/**
|
||||
|
@ -14,24 +19,146 @@ class AbstractApi
|
|||
*/
|
||||
protected $client;
|
||||
|
||||
public function __construct($httpClient)
|
||||
/**
|
||||
* @var bool Use HTTPS instead of HTTP
|
||||
*/
|
||||
protected $useHttps;
|
||||
|
||||
/**
|
||||
* @param Client $httpClient HTTP-client from Guzzle
|
||||
* @param bool $https Use HTTPS instead of HTTP
|
||||
* @param string $baseUrl Base URL for API
|
||||
*/
|
||||
public function __construct(Client $httpClient, $https = true, $baseUrl = null)
|
||||
{
|
||||
$this->client = $httpClient;
|
||||
$this->useHttps = ($https) ? true : false;
|
||||
|
||||
if ($baseUrl) {
|
||||
$this->setBaseUrl($baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make GET request and return Response object
|
||||
*
|
||||
* @param string $pathTemplate
|
||||
* @param array $parameters
|
||||
**
|
||||
* @param string $path Request path
|
||||
* @param array $parameters Key => Value array of query parameters
|
||||
* @return GuzzleResponse
|
||||
*/
|
||||
public function sendGetRequest($pathTemplate, array $parameters = [])
|
||||
public function sendGetRequest($path, array $parameters = [])
|
||||
{
|
||||
$path = vsprintf($pathTemplate, $parameters);
|
||||
|
||||
/** @var $request GuzzleRequest */
|
||||
$request = $this->client->get($path);
|
||||
|
||||
$query = $request->getQuery();
|
||||
|
||||
foreach ($parameters as $parameter => $value) {
|
||||
$query->set($parameter, $value);
|
||||
}
|
||||
|
||||
return $request->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make GET request and return data from response
|
||||
*
|
||||
* @param string $path Path template
|
||||
* @param array $parameters Parameters array used to fill path template
|
||||
* @param bool $decodeJsonResponse Decode JSON or return plaintext
|
||||
* @param bool $decodeJsonToObjects Decode JSON objects to PHP objects instead of arrays
|
||||
* @return array|string
|
||||
*/
|
||||
public function getGetRequestData($path, array $parameters = [], $decodeJsonResponse = false, $decodeJsonToObjects = false)
|
||||
{
|
||||
$response = $this->sendGetRequest($path, $parameters);
|
||||
|
||||
if ($decodeJsonResponse) {
|
||||
if ($decodeJsonToObjects) {
|
||||
return json_decode($response->getBody(true));
|
||||
} else {
|
||||
return $response->json();
|
||||
}
|
||||
} else {
|
||||
return $response->getBody(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make POST request and return data from response
|
||||
* @todo implement method
|
||||
*/
|
||||
public function getPostRequestData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTTP client base URL
|
||||
*
|
||||
* @return string Base URL of client
|
||||
*/
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->client->getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set HTTP client base URL
|
||||
*
|
||||
* @param string $baseUrl Base URL of API
|
||||
* @param bool $useProtocol Do not change URL scheme (http/https) defined in $baseUrl
|
||||
* @return $this
|
||||
*/
|
||||
public function setBaseUrl($baseUrl, $useProtocol = false)
|
||||
{
|
||||
// Overriding protocol
|
||||
if (!$useProtocol) {
|
||||
$baseUrl = str_replace(['http://', 'https://',], ($this->useHttps) ? 'https://' : 'http://', $baseUrl);
|
||||
}
|
||||
// Adding missing protocol
|
||||
if ((false === strpos(strtolower($baseUrl), 'http://')) && (false === strpos(strtolower($baseUrl), 'https://'))) {
|
||||
$baseUrl = (($this->useHttps) ? 'https://' : 'http://') . $baseUrl;
|
||||
}
|
||||
|
||||
$this->client->setBaseUrl($baseUrl);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if API service uses HTTPS
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isHttps()
|
||||
{
|
||||
return $this->useHttps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable HTTPS
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enableHttps()
|
||||
{
|
||||
$this->useHttps = true;
|
||||
$this->setBaseUrl($this->getBaseUrl());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable HTTPS
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableHttps()
|
||||
{
|
||||
$this->useHttps = false;
|
||||
$this->setBaseUrl($this->getBaseUrl());
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Service;
|
||||
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\Subscription;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionEvent;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||
|
||||
class SubscriptionsManager
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->em = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param User[]|array $newSubscribersList
|
||||
*/
|
||||
public function updateUserSubscribers(User $user, $newSubscribersList = [])
|
||||
{
|
||||
/** @var Subscription[] $tmpOldSubscribers */
|
||||
$tmpOldSubscribers = $user->getSubscribers();
|
||||
|
||||
$oldSubscribersList = [];
|
||||
|
||||
foreach ($tmpOldSubscribers as $subscription) {
|
||||
$oldSubscribersList[] = $subscription->getSubscriber();
|
||||
}
|
||||
|
||||
unset($tmpOldSubscribers);
|
||||
|
||||
$subscribedList = $this->getUsersListsDiff($newSubscribersList, $oldSubscribersList);
|
||||
$unsubscribedList = $this->getUsersListsDiff($oldSubscribersList, $newSubscribersList);
|
||||
|
||||
/** @var User $subscribedUser */
|
||||
foreach ($subscribedList as $subscribedUser) {
|
||||
$subscription = new Subscription();
|
||||
$subscription
|
||||
->setAuthor($user)
|
||||
->setSubscriber($subscribedUser)
|
||||
;
|
||||
|
||||
$user->addSubscriber($subscription);
|
||||
|
||||
$logEvent = new SubscriptionEvent();
|
||||
$logEvent
|
||||
->setSubscriber($subscribedUser)
|
||||
->setAuthor($user)
|
||||
->setAction(SubscriptionEvent::ACTION_SUBSCRIBE)
|
||||
;
|
||||
|
||||
$user->addNewSubscriberEvent($logEvent);
|
||||
|
||||
$this->em->persist($subscription);
|
||||
$this->em->persist($logEvent);
|
||||
}
|
||||
|
||||
unset($subscribedList);
|
||||
|
||||
/** @var QueryBuilder $unsubscribedQuery */
|
||||
$unsubscribedQuery = $this->em->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s');
|
||||
$unsubscribedQuery
|
||||
->delete()
|
||||
->where('s.author = :author')
|
||||
->andWhere('s.subscriber IN (:subscribers)')
|
||||
;
|
||||
|
||||
|
||||
/** @var User $unsubscribedUser */
|
||||
foreach ($unsubscribedList as $unsubscribedUser) {
|
||||
$logEvent = new SubscriptionEvent();
|
||||
$logEvent
|
||||
->setSubscriber($unsubscribedUser)
|
||||
->setAction($user)
|
||||
->setAction(SubscriptionEvent::ACTION_UNSUBSCRIBE)
|
||||
;
|
||||
|
||||
$user->addNewSubscriberEvent($logEvent);
|
||||
|
||||
$this->em->persist($logEvent);
|
||||
}
|
||||
|
||||
$unsubscribedQuery
|
||||
->setParameter('author', $user->getId())
|
||||
->setParameter('subscribers', $unsubscribedList)
|
||||
->getQuery()->execute();
|
||||
;
|
||||
|
||||
unset($unsubscribedList);
|
||||
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares $list1 against $list2 and returns the values in $list1 that are not present in $list2.
|
||||
*
|
||||
* @param User[] $list1
|
||||
* @param User[] $list2
|
||||
* @return User[] Diff
|
||||
*/
|
||||
public function getUsersListsDiff(array $list1 = [], array $list2 = [])
|
||||
{
|
||||
$hash1 = [];
|
||||
$hash2 = [];
|
||||
|
||||
foreach ($list1 as $user) {
|
||||
$hash1[$user->getId()] = $user;
|
||||
}
|
||||
foreach ($list2 as $user) {
|
||||
$hash2[$user->getId()] = $user;
|
||||
}
|
||||
|
||||
return array_diff_key($hash1, $hash2);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Service;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Guzzle\Service\Client;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||
|
||||
/**
|
||||
|
@ -13,10 +17,27 @@ class UserApi extends AbstractApi
|
|||
const PATH_USER_SUBSCRIPTIONS = '/api/user/%s/subscriptions';
|
||||
const PATH_USER_SUBSCRIBERS = '/api/user/%s/subscribers';
|
||||
|
||||
const AVATAR_SIZE_SMALL = '24';
|
||||
const AVATAR_SIZE_MEDIUM = '40';
|
||||
const AVATAR_SIZE_LARGE = '80';
|
||||
|
||||
/**
|
||||
* @var string Base URL for user avatars
|
||||
*/
|
||||
protected $avatarsBaseUrl = '//i.point.im/a/';
|
||||
protected $avatarsBaseUrl = 'point.im/avatar/';
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
|
||||
public function __construct(Client $httpClient, $https = true, $baseUrl = null, EntityManagerInterface $entityManager)
|
||||
{
|
||||
parent::__construct($httpClient, $https, $baseUrl);
|
||||
|
||||
$this->em = $entityManager;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
|
@ -24,33 +45,68 @@ class UserApi extends AbstractApi
|
|||
}
|
||||
|
||||
/**
|
||||
* Get user subscribers by his/her name
|
||||
* Get user subscribers by user login
|
||||
*
|
||||
* @param string $login
|
||||
* @return User[]
|
||||
*/
|
||||
public function getUserSubscribersByLogin($login)
|
||||
{
|
||||
$response = $this->sendGetRequest(self::PATH_USER_SUBSCRIBERS, [$login]);
|
||||
$usersList = $this->getGetRequestData('/api/user/' . $login . '/subscribers', [], true);
|
||||
|
||||
$body = $response->getBody(true);
|
||||
|
||||
// @todo use JMSSerializer
|
||||
$data = json_decode($body);
|
||||
|
||||
$users = [];
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $apiUser) {
|
||||
$user = new User();
|
||||
$user->setId($apiUser->id);
|
||||
$user->setLogin($apiUser->login);
|
||||
$user->setName($apiUser->name);
|
||||
|
||||
$users[] = $user;
|
||||
}
|
||||
}
|
||||
$users = $this->getUsersFromList($usersList);
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User[]
|
||||
*/
|
||||
private function getUsersFromList(array $users = [])
|
||||
{
|
||||
if (!is_array($users)) {
|
||||
throw new \InvalidArgumentException('$users must be an array');
|
||||
}
|
||||
|
||||
/** @var EntityRepository $userRepo */
|
||||
$userRepo = $this->em->getRepository('SkobkinPointToolsBundle:User');
|
||||
|
||||
$resultUsers = [];
|
||||
|
||||
foreach ($users as $userData) {
|
||||
if (array_key_exists('id', $userData) && array_key_exists('login', $userData) && array_key_exists('name', $userData) && is_numeric($userData['id'])) {
|
||||
|
||||
// @todo Optimize with prehashed id's list
|
||||
$user = $userRepo->findOneBy(['id' => $userData['id']]);
|
||||
|
||||
if (!$user) {
|
||||
$user = new User();
|
||||
$user->setId((int) $userData['id']);
|
||||
$this->em->persist($user);
|
||||
}
|
||||
|
||||
// Updating data
|
||||
if ($user->getLogin() !== $userData['login']) {
|
||||
$user->setLogin($userData['login']);
|
||||
}
|
||||
if ($user->getName() !== $userData['name']) {
|
||||
$user->setName($userData['name']);
|
||||
}
|
||||
|
||||
$resultUsers[] = $user;
|
||||
}
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
return $resultUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $login
|
||||
*/
|
||||
public function getAvatarUrl(User $user, $size)
|
||||
{
|
||||
return ($this->useHttps ? 'https://' : 'http://') . $this->avatarsBaseUrl . $user->getLogin() . '/' . $size;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue