fix #30. Factories and API clients refactored to DTO. Some other changes.

This commit is contained in:
Alexey Skobkin 2017-01-15 05:54:38 +03:00
parent 80a7cc47be
commit 6fc01076e2
46 changed files with 1297 additions and 1037 deletions

View file

@ -8,7 +8,7 @@ use Skobkin\Bundle\PointToolsBundle\Entity\Subscription;
use Skobkin\Bundle\PointToolsBundle\Entity\User; use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository; use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
use Skobkin\Bundle\PointToolsBundle\Service\SubscriptionsManager; use Skobkin\Bundle\PointToolsBundle\Service\SubscriptionsManager;
use Skobkin\Bundle\PointToolsBundle\Service\UserApi; use Skobkin\Bundle\PointToolsBundle\Service\Api\UserApi;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;

View file

@ -21,7 +21,7 @@ class CrawlerController extends AbstractApiController
$serializer = $this->get('jms_serializer'); $serializer = $this->get('jms_serializer');
$page = $serializer->deserialize($json, 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\PostsPage', 'json'); $page = $serializer->deserialize($json, 'Skobkin\Bundle\PointToolsBundle\DTO\Api\PostsPage', 'json');
/** @var PostFactory $factory */ /** @var PostFactory $factory */
$factory = $this->get('app.point.post_factory'); $factory = $this->get('app.point.post_factory');

View file

@ -2,35 +2,20 @@
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api; namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
use JMS\Serializer\Annotation as JMSS; class Auth implements ValidableInterface
/**
* @JMSS\ExclusionPolicy("none")
* @JMSS\AccessType("public_method")
*/
class Auth
{ {
/** /**
* @var string * @var string|null
*
* @JMSS\SerializedName("token")
* @JMSS\Type("string")
*/ */
private $token; private $token;
/** /**
* @var string * @var string|null
*
* @JMSS\SerializedName("csrf_token")
* @JMSS\Type("string")
*/ */
private $csRfToken; private $csRfToken;
/** /**
* @var string * @var string|null
*
* @JMSS\SerializedName("error")
* @JMSS\Type("string")
*/ */
private $error; private $error;
@ -40,11 +25,9 @@ class Auth
return $this->token; return $this->token;
} }
public function setToken(?string $token): self public function setToken(?string $token): void
{ {
$this->token = $token; $this->token = $token;
return $this;
} }
public function getCsRfToken(): ?string public function getCsRfToken(): ?string
@ -52,11 +35,9 @@ class Auth
return $this->csRfToken; return $this->csRfToken;
} }
public function setCsRfToken(?string $csRfToken): self public function setCsRfToken(?string $csRfToken): void
{ {
$this->csRfToken = $csRfToken; $this->csRfToken = $csRfToken;
return $this;
} }
public function getError(): ?string public function getError(): ?string
@ -64,10 +45,17 @@ class Auth
return $this->error; return $this->error;
} }
public function setError(?string $error): self public function setError(?string $error): void
{ {
$this->error = $error; $this->error = $error;
}
return $this; public function isValid(): bool
{
if (null !== $this->token && null !== $this->csRfToken && null === $this->error) {
return true;
}
return false;
} }
} }

View file

@ -0,0 +1,126 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
class Comment implements ValidableInterface
{
/**
* @var string|null
*/
private $postId;
/**
* @var int|null
*/
private $number;
/**
* @var int|null
*/
private $toCommentId;
/**
* @var string|null
*/
private $created;
/**
* @var string|null
*/
private $text;
/**
* @var User|null
*/
private $author;
/**
* @var bool|null
*/
private $isRec;
public function getPostId(): ?string
{
return $this->postId;
}
public function setPostId(?string $postId): void
{
$this->postId = $postId;
}
public function getNumber(): ?int
{
return $this->number;
}
public function setNumber(?int $number): void
{
$this->number = $number;
}
public function getToCommentId(): ?int
{
return $this->toCommentId;
}
public function setToCommentId(?int $toCommentId): void
{
$this->toCommentId = $toCommentId;
}
public function getCreated(): string
{
return $this->created;
}
public function setCreated(?string $created): void
{
$this->created = $created;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(?string $text): void
{
$this->text = $text;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): void
{
$this->author = $author;
}
public function isIsRec(): ?bool
{
return $this->isRec;
}
public function getIsRec(): ?bool
{
return $this->isRec;
}
public function setIsRec(?bool $isRec): void
{
$this->isRec = $isRec;
}
public function isValid(): bool
{
if (null !== $this->postId && null !== $this->number && null !== $this->author && null !== $this->text) {
return true;
}
return false;
}
}

View file

@ -1,37 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler;
use JMS\Serializer\Annotation as JMSS;
/**
* @JMSS\ExclusionPolicy("none")
* @JMSS\AccessType("public_method")
*/
class MetaPost
{
/**
* @var Post
*
* @JMSS\SerializedName("post")
* @JMSS\Type("Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\Post")
* @JMSS\MaxDepth(2)
*/
private $post;
/**
* @return Post
*/
public function getPost(): ?Post
{
return $this->post;
}
public function setPost(Post $post): self
{
$this->post = $post;
return $this;
}
}

View file

@ -1,187 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler;
use JMS\Serializer\Annotation as JMSS;
/**
* @JMSS\ExclusionPolicy("none")
* @JMSS\AccessType("public_method")
*/
class Post
{
/**
* @var string
*
* @JMSS\SerializedName("id")
* @JMSS\Type("string")
*/
private $id;
/**
* @var string[]
*
* @JMSS\SerializedName("tags")
* @JMSS\Type("array<string>")
*/
private $tags;
/**
* @var string[]
*
* @JMSS\SerializedName("files")
* @JMSS\Type("array<string>")
*/
private $files;
/**
* @var User
*
* @JMSS\SerializedName("author")
* @JMSS\Type("Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\User")
* @JMSS\MaxDepth(1)
*/
private $author;
/**
* @var string
*
* @JMSS\SerializedName("text")
* @JMSS\Type("string")
*/
private $text;
/**
* @var string
*
* @JMSS\SerializedName("created")
* @JMSS\Type("string")
*/
private $created;
/**
* @var string
*
* @JMSS\SerializedName("type")
* @JMSS\Type("string")
*/
private $type;
/**
* @var bool
*
* @JMSS\SerializedName("private")
* @JMSS\Type("boolean")
*/
private $private;
public function getId(): ?string
{
return $this->id;
}
public function setId(?string $id): self
{
$this->id = $id;
return $this;
}
/**
* @return string[]
*/
public function getTags(): ?array
{
return $this->tags;
}
public function setTags(?array $tags): self
{
$this->tags = $tags;
return $this;
}
/**
* @return string[]
*/
public function getFiles(): ?array
{
return $this->files;
}
/**
* @param string[] $files
*
* @return Post
*/
public function setFiles(?array $files): self
{
$this->files = $files;
return $this;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(?string $text): self
{
$this->text = $text;
return $this;
}
public function getCreated(): ?string
{
return $this->created;
}
public function setCreated(?string $created): self
{
$this->created = $created;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): self
{
$this->type = $type;
return $this;
}
public function getPrivate(): ?bool
{
return $this->private;
}
public function isPrivate(): ?bool
{
return $this->private;
}
public function setPrivate(?bool $private): self
{
$this->private = $private;
return $this;
}
}

View file

@ -1,44 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler;
use JMS\Serializer\Annotation as JMSS;
/**
* @JMSS\ExclusionPolicy("none")
* @JMSS\AccessType("public_method")
*/
class PostsPage
{
/**
* @var MetaPost[]
*
* @JMSS\SerializedName("posts")
* @JMSS\Type("array<Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\MetaPost>")
* @JMSS\MaxDepth(3)
*/
private $posts;
/**
* @return MetaPost[]
*/
public function getPosts(): ?array
{
return $this->posts;
}
/**
* @todo move to constructor
*
* @param MetaPost[] $posts
* @return PostsPage
*/
public function setPosts(?array $posts): self
{
$this->posts = $posts;
return $this;
}
}

View file

@ -1,70 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler;
use JMS\Serializer\Annotation as JMSS;
/**
* @JMSS\ExclusionPolicy("none")
* @JMSS\AccessType("public_method")
*/
class User
{
/**
* @var string
*
* @JMSS\SerializedName("id")
* @JMSS\Type("integer")
*/
private $id;
/**
* @var string
*
* @JMSS\SerializedName("login")
* @JMSS\Type("string")
*/
private $login;
/**
* @var string
*
* @JMSS\SerializedName("name")
* @JMSS\Type("string")
*/
private $name;
public function getId(): ?string
{
return $this->id;
}
public function setId(?string $id): self
{
$this->id = $id;
return $this;
}
public function getLogin(): ?string
{
return $this->login;
}
public function setLogin(?string $login): self
{
$this->login = $login;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(?string $name): self
{
$this->name = $name;
return $this;
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
class MetaPost implements ValidableInterface
{
/**
* @var Post|null
*/
private $post;
/**
* @var Comment[]|null
*/
private $comments;
public function getPost(): ?Post
{
return $this->post;
}
public function setPost(?Post $post): void
{
$this->post = $post;
}
/**
* @return Comment[]|null
*/
public function getComments(): ?array
{
return $this->comments;
}
/**
* @param Comment[]|null $comments
*/
public function setComments(?array $comments): void
{
$this->comments = $comments;
}
public function isValid(): bool
{
if (null !== $this->post && $this->post->isValid()) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,157 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
class Post implements ValidableInterface
{
/**
* @var string|null
*/
private $id;
/**
* @var string[]|null
*/
private $tags;
/**
* @var string[]|null
*/
private $files;
/**
* @var User|null
*/
private $author;
/**
* @var string|null
*/
private $text;
/**
* @var string|null
*/
private $created;
/**
* @var string|null
*/
private $type;
/**
* @var bool|null
*/
private $private;
public function getId(): ?string
{
return $this->id;
}
public function setId(?string $id): void
{
$this->id = $id;
}
/**
* @return string[]|null
*/
public function getTags(): ?array
{
return $this->tags;
}
public function setTags(?array $tags): void
{
$this->tags = $tags;
}
/**
* @return string[]|null
*/
public function getFiles(): ?array
{
return $this->files;
}
/**
* @param string[]|null $files
*/
public function setFiles(?array $files): void
{
$this->files = $files;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): void
{
$this->author = $author;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(?string $text): void
{
$this->text = $text;
}
public function getCreated(): ?string
{
return $this->created;
}
public function setCreated(?string $created): void
{
$this->created = $created;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): void
{
$this->type = $type;
}
public function getPrivate(): ?bool
{
return $this->private;
}
public function isPrivate(): ?bool
{
return $this->private;
}
public function setPrivate(?bool $private): void
{
$this->private = $private;
}
public function isValid(): bool
{
if (
null !== $this->id &&
null !== $this->author &&
$this->author->isValid() &&
null !== $this->text &&
null !== $this->created &&
null !== $this->type
) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
class PostsPage implements ValidableInterface
{
/**
* @var MetaPost[]|null
*/
private $posts;
/**
* @var bool|null
*/
private $hasNext;
/**
* @return MetaPost[]|null
*/
public function getPosts(): ?array
{
return $this->posts;
}
/**
* @param MetaPost[]|null $posts
*/
public function setPosts(?array $posts): void
{
$this->posts = $posts;
}
public function getHasNext(): ?bool
{
return $this->hasNext;
}
public function setHasNext(?bool $hasNext): void
{
$this->hasNext = $hasNext;
}
public function isValid(): bool
{
if (null !== $this->posts) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,226 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
class User implements ValidableInterface
{
/**
* @var string|null
*/
private $id;
/**
* @var string|null
*/
private $login;
/**
* @var string|null
*/
private $name;
/**
* @var string|null
*/
private $about;
/**
* @var string|null
*/
private $xmpp;
/**
* @var string|null
*/
private $created;
/**
* @var bool|null
*/
private $gender;
/**
* @var bool|null
*/
private $denyAnonymous;
/**
* @var bool|null
*/
private $private;
/**
* @var string|null
*/
private $birthDate;
/**
* @var string|null
*/
private $homepage;
/**
* @var string|null
*/
private $email;
/**
* @var string|null
*/
private $location;
/**
* @var string|null
*/
private $error;
public function getId(): ?string
{
return $this->id;
}
public function setId(?string $id): void
{
$this->id = $id;
}
public function getLogin(): ?string
{
return $this->login;
}
public function setLogin(?string $login): void
{
$this->login = $login;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(?string $name): void
{
$this->name = $name;
}
public function getAbout(): ?string
{
return $this->about;
}
public function setAbout(?string $about): void
{
$this->about = $about;
}
public function getXmpp(): ?string
{
return $this->xmpp;
}
public function setXmpp(?string $xmpp): void
{
$this->xmpp = $xmpp;
}
public function getCreated(): ?string
{
return $this->created;
}
public function setCreated(?string $created): void
{
$this->created = $created;
}
public function getGender(): ?bool
{
return $this->gender;
}
public function setGender(?bool $gender): void
{
$this->gender = $gender;
}
public function getDenyAnonymous(): ?bool
{
return $this->denyAnonymous;
}
public function setDenyAnonymous(?bool $denyAnonymous): void
{
$this->denyAnonymous = $denyAnonymous;
}
public function getPrivate(): ?bool
{
return $this->private;
}
public function setPrivate(?bool $private): void
{
$this->private = $private;
}
public function getBirthDate(): ?string
{
return $this->birthDate;
}
public function setBirthDate(?string $birthDate): void
{
$this->birthDate = $birthDate;
}
public function getHomepage(): ?string
{
return $this->homepage;
}
public function setHomepage(?string $homepage): void
{
$this->homepage = $homepage;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(?string $email): void
{
$this->email = $email;
}
public function getLocation(): ?string
{
return $this->location;
}
public function setLocation(?string $location): void
{
$this->location = $location;
}
public function getError(): ?string
{
return $this->error;
}
public function setError(?string $error): void
{
$this->error = $error;
}
public function isValid(): bool
{
if (null === $this->error && null !== $this->id && null !== $this->login) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
interface ValidableInterface
{
public function isValid(): bool;
}

View file

@ -0,0 +1,8 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class ApiException extends \Exception
{
}

View file

@ -0,0 +1,8 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class ForbiddenException extends ApiException
{
}

View file

@ -1,7 +1,6 @@
<?php <?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Exceptions; namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class InvalidResponseException extends ApiException class InvalidResponseException extends ApiException
{ {

View file

@ -0,0 +1,8 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class NetworkException extends ApiException
{
}

View file

@ -0,0 +1,8 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class NotFoundException extends ApiException
{
}

View file

@ -0,0 +1,8 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class ServerProblemException extends ApiException
{
}

View file

@ -0,0 +1,8 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class UnauthorizedException extends ApiException
{
}

View file

@ -1,7 +1,6 @@
<?php <?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Exceptions; namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
class UserNotFoundException extends ApiException class UserNotFoundException extends ApiException
{ {

View file

@ -2,8 +2,7 @@
namespace Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog; namespace Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog;
use Exception; use Skobkin\Bundle\PointToolsBundle\DTO\Api\Post;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\Post;
class InvalidPostDataException extends InvalidDataException class InvalidPostDataException extends InvalidDataException
{ {
@ -12,7 +11,7 @@ class InvalidPostDataException extends InvalidDataException
*/ */
private $post; private $post;
public function __construct($message = '', Post $post, $code = 0, Exception $previous = null) public function __construct($message = '', Post $post, $code = 0, \Exception $previous = null)
{ {
$this->post = $post; $this->post = $post;

View file

@ -0,0 +1,25 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Exception\Factory;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\User as UserDTO;
class InvalidUserDataException extends \Exception
{
/**
* @var UserDTO
*/
private $user;
public function __construct($message = "", UserDTO $user, $code = 0, \Exception $previous = null)
{
$this->user = $user;
parent::__construct($message, $code, $previous);
}
public function getUser(): UserDTO
{
return $this->user;
}
}

View file

@ -1,7 +1,6 @@
<?php <?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Exceptions; namespace Skobkin\Bundle\PointToolsBundle\Exception;
class SubscriptionManagerException extends \Exception class SubscriptionManagerException extends \Exception
{ {

View file

@ -0,0 +1,11 @@
Skobkin\Bundle\PointToolsBundle\DTO\Api\Auth:
exclusion_policy: none
access_type: public_method
properties:
token:
type: string
csRfToken:
type: string
serialized_name: 'csrf_token'
error:
type: string

View file

@ -0,0 +1,23 @@
Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment:
exclusion_policy: none
access_type: public_method
properties:
postId:
serialized_name: 'post_id'
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post'
max_depth: 2
number:
serialized_name: 'id'
type: integer
toCommentId:
serialized_name: 'to_comment_id'
type: integer
created:
type: string
text:
type: string
author:
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\User'
isRec:
serialized_name: 'is_rec'
type: boolean

View file

@ -0,0 +1,10 @@
Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost:
exclusion_policy: none
access_type: public_method
properties:
post:
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post'
max_depth: 2
comments:
type: 'array<Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment>'
max_depth: 2

View file

@ -0,0 +1,25 @@
Skobkin\Bundle\PointToolsBundle\DTO\Api\Post:
exclusion_policy: none
access_type: public_method
properties:
id:
type: string
#groups: []
tags:
type: 'array<string>'
files:
type: 'array<string>'
author:
type: Skobkin\Bundle\PointToolsBundle\DTO\Api\User
max_depth: 1
text:
type: string
created:
type: string
type:
type: string
private:
type: boolean
#callback_methods:
# post_deserialize: [foo, bar]

View file

@ -0,0 +1,7 @@
Skobkin\Bundle\PointToolsBundle\DTO\Api\PostPage:
exclusion_policy: none
access_type: public_method
properties:
posts:
type: 'array<Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost>'
max_depth: 3

View file

@ -0,0 +1,49 @@
Skobkin\Bundle\PointToolsBundle\DTO\Api\User:
exclusion_policy: none
access_type: public_method
properties:
id:
type: integer
groups: [user_short, user_full]
login:
type: string
groups: [user_short, user_full]
name:
type: string
groups: [user_short, user_full]
about:
type: string
groups: [user_full]
xmpp:
type: string
groups: [user_full]
created:
type: string
groups: [user_full]
gender:
type: boolean
groups: [user_full]
denyAnonymous:
serialized_name: 'deny_anonymous'
type: boolean
groups: [user_full]
private:
type: boolean
groups: [user_full]
birthDate:
serialized_name: 'birthdate'
type: string
groups: [user_full]
homepage:
type: string
groups: [user_full]
email:
type: string
groups: [user_full]
location:
type: string
groups: [user_full]
# TODO automatically convert string date to DateTime
#callback_methods:
# post_deserialize: [foo, bar]

View file

@ -20,20 +20,24 @@ services:
abstract: true abstract: true
arguments: arguments:
- '@app.http.point_client' - '@app.http.point_client'
- '@jms_serializer'
- '@logger' - '@logger'
# User
app.point.api_user: app.point.api_user:
class: Skobkin\Bundle\PointToolsBundle\Service\UserApi class: Skobkin\Bundle\PointToolsBundle\Service\Api\UserApi
parent: app.point.abstract_api parent: app.point.abstract_api
arguments: arguments:
- '@app.point.user_factory' - '@app.point.user_factory'
- '@jms_serializer' tags:
- { name: monolog.logger, channel: point_user_api }
# Post
app.point.api_post: app.point.api_post:
class: Skobkin\Bundle\PointToolsBundle\Service\PostApi class: Skobkin\Bundle\PointToolsBundle\Service\Api\PostApi
parent: app.point.abstract_api parent: app.point.abstract_api
arguments: arguments:
- '@app.point.post_factory' - '@app.point.post_factory'
tags:
- { name: monolog.logger, channel: point_post_api }
# Point subscription manager # Point subscription manager
@ -116,44 +120,48 @@ services:
# Factories # Factories
# User factory # Abstract factory
app.point.abstract_factory:
abstract: true
arguments: [ '@logger' ]
# User
app.point.user_factory: app.point.user_factory:
class: Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory class: Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory
parent: app.point.abstract_factory
arguments: [ '@app.point.user_repository' ] arguments: [ '@app.point.user_repository' ]
# Comment
# Comment factory
app.point.comment_factory: app.point.comment_factory:
class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\CommentFactory class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\CommentFactory
parent: app.point.abstract_factory
arguments: arguments:
- '@app.point.comment_repository' - '@app.point.comment_repository'
- '@app.point.post_repository' - '@app.point.post_repository'
- '@app.point.user_factory' - '@app.point.user_factory'
# Tag
# Tag factory
app.point.tag_factory: app.point.tag_factory:
class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\TagFactory class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\TagFactory
arguments: [ '@logger', '@app.point.tag_repository' ] parent: app.point.abstract_factory
arguments: [ '@app.point.tag_repository' ]
# File factory # File
app.point.file_factory: app.point.file_factory:
class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\FileFactory class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\FileFactory
arguments: [ '@logger', '@app.point.file_repository' ] parent: app.point.abstract_factory
arguments: [ '@app.point.file_repository' ]
# Post factory # Post
app.point.post_factory: app.point.post_factory:
class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory
parent: app.point.abstract_factory
arguments: arguments:
- '@logger'
- '@doctrine.orm.entity_manager' - '@doctrine.orm.entity_manager'
- '@app.point.post_repository' - '@app.point.post_repository'
- '@app.point.user_factory' - '@app.point.user_factory'
- '@app.point.file_factory' - '@app.point.file_factory'
- '@app.point.comment_factory' - '@app.point.comment_factory'
- '@app.point.tag_factory' - '@app.point.tag_factory'
# Telegram account
# Telegram accounts factory
app.telegram.telegram_account_factory: app.telegram.telegram_account_factory:
class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory class: Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory
parent: app.point.abstract_factory
arguments: ['@app.point.telegram_account_repository'] arguments: ['@app.point.telegram_account_repository']

View file

@ -1,120 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service;
use GuzzleHttp\ClientInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
/**
* @todo Refactor DTO deserialization
*/
class AbstractApi
{
/**
* @var ClientInterface HTTP-client from Guzzle
*/
protected $client;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var string Authentication token for API
*/
protected $authToken;
/**
* @var string CSRF-token for API
*/
protected $csRfToken;
public function __construct(ClientInterface $httpClient, LoggerInterface $logger)
{
$this->client = $httpClient;
$this->logger = $logger;
}
/**
* @param string $path Request path
* @param array $parameters Key => Value array of query parameters
*
* @return ResponseInterface
*/
public function sendGetRequest(string $path, array $parameters = []): ResponseInterface
{
$this->logger->debug('Sending GET request', ['path' => $path, 'parameters' => $parameters]);
return $this->client->request('GET', $path, ['query' => $parameters]);
}
/**
* @param string $path Request path
* @param array $parameters Key => Value array of request data
*
* @return ResponseInterface
*/
public function sendPostRequest(string $path, array $parameters = []): ResponseInterface
{
$this->logger->debug('Sending POST request', ['path' => $path, 'parameters' => $parameters]);
return $this->client->request('POST', $path, ['form_params' => $parameters]);
}
/**
* 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 mixed
*/
public function getGetRequestData($path, array $parameters = [], bool $decodeJsonResponse = false, bool $decodeJsonToObjects = false)
{
$response = $this->sendGetRequest($path, $parameters);
return $this->processResponse($response, $decodeJsonResponse, $decodeJsonToObjects);
}
/**
* Make POST request and return data from response
*
* @param string $path Path template
* @param array $parameters Parameters array used to fill path template
* @param bool $decodeJson Decode JSON or return plaintext
* @param bool $decodeToObjects Decode JSON objects to PHP objects instead of arrays
*
* @return mixed
*/
public function getPostRequestData($path, array $parameters = [], bool $decodeJson = false, bool $decodeToObjects = false)
{
$response = $this->sendPostRequest($path, $parameters);
return $this->processResponse($response, $decodeJson, $decodeToObjects);
}
/**
* @param ResponseInterface $response
* @param bool $decodeJson
* @param bool $decodeToObjects
*
* @return string|array|object
*/
private function processResponse(ResponseInterface $response, bool $decodeJson = false, bool $decodeToObjects = false)
{
if ($decodeJson) {
if ($decodeToObjects) {
return json_decode($response->getBody());
} else {
return json_decode($response->getBody(), true);
}
} else {
return $response->getBody();
}
}
}

View file

@ -0,0 +1,176 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\TransferException;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Serializer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\ForbiddenException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\NetworkException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\NotFoundException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\ServerProblemException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\UnauthorizedException;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
class AbstractApi
{
/**
* @var ClientInterface HTTP-client from Guzzle
*/
protected $client;
/**
* @var Serializer
*/
protected $serializer;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var string Authentication token for API
*/
protected $authToken;
/**
* @var string CSRF-token for API
*/
protected $csRfToken;
public function __construct(ClientInterface $httpClient, Serializer $serializer, LoggerInterface $logger)
{
$this->client = $httpClient;
$this->serializer = $serializer;
$this->logger = $logger;
}
/**
* @param string $path Request path
* @param array $parameters Key => Value array of query parameters
*
* @return ResponseInterface
*
* @throws NetworkException
*/
public function sendGetRequest(string $path, array $parameters = []): ResponseInterface
{
$this->logger->debug('Sending GET request', ['path' => $path, 'parameters' => $parameters]);
try {
return $this->client->request('GET', $path, ['query' => $parameters]);
} catch (TransferException $e) {
throw new NetworkException('Request error', $e->getCode(), $e);
}
}
/**
* @param string $path Request path
* @param array $parameters Key => Value array of request data
*
* @return ResponseInterface
*
* @throws NetworkException
*/
public function sendPostRequest(string $path, array $parameters = []): ResponseInterface
{
$this->logger->debug('Sending POST request', ['path' => $path, 'parameters' => $parameters]);
try {
return $this->client->request('POST', $path, ['form_params' => $parameters]);
} catch (TransferException $e) {
throw new NetworkException('Request error', $e->getCode(), $e);
}
}
/**
* Make GET request and return response body
*/
public function getGetResponseBody($path, array $parameters = []): StreamInterface
{
$response = $this->sendGetRequest($path, $parameters);
$this->checkResponse($response);
return $response->getBody();
}
/**
* Make POST request and return response body
*/
public function getPostResponseBody(string $path, array $parameters = []): StreamInterface
{
$response = $this->sendPostRequest($path, $parameters);
$this->checkResponse($response);
return $response->getBody();
}
/**
* Make GET request and return DTO objects
*
* @return array|object
*/
public function getGetJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null)
{
return $this->serializer->deserialize(
$this->getGetResponseBody($path, $parameters),
$type,
'json',
$context
);
}
/**
* Make POST request and return DTO objects
*
* @return array|object
*/
public function getPostJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null)
{
return $this->serializer->deserialize(
$this->getPostResponseBody($path, $parameters),
$type,
'json',
$context
);
}
/**
* @throws ForbiddenException
* @throws NotFoundException
* @throws ServerProblemException
* @throws UnauthorizedException
*/
private function checkResponse(ResponseInterface $response): void
{
$code = $response->getStatusCode();
$reason = $response->getReasonPhrase();
switch ($code) {
case SymfonyResponse::HTTP_UNAUTHORIZED:
throw new UnauthorizedException($reason, $code);
break;
case SymfonyResponse::HTTP_FORBIDDEN:
throw new ForbiddenException($reason, $code);
break;
case SymfonyResponse::HTTP_NOT_FOUND:
throw new NotFoundException($reason, $code);
break;
case SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR:
case SymfonyResponse::HTTP_NOT_IMPLEMENTED:
case SymfonyResponse::HTTP_BAD_GATEWAY:
case SymfonyResponse::HTTP_SERVICE_UNAVAILABLE:
case SymfonyResponse::HTTP_GATEWAY_TIMEOUT:
throw new ServerProblemException($reason, $code);
break;
}
}
}

View file

@ -1,8 +1,9 @@
<?php <?php
namespace Skobkin\Bundle\PointToolsBundle\Service; namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
use GuzzleHttp\ClientInterface; use GuzzleHttp\ClientInterface;
use JMS\Serializer\Serializer;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory; use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory;
@ -17,9 +18,9 @@ class PostApi extends AbstractApi
private $postFactory; private $postFactory;
public function __construct(ClientInterface $httpClient, LoggerInterface $logger, PostFactory $postFactory) public function __construct(ClientInterface $httpClient, Serializer $serializer, LoggerInterface $logger, PostFactory $postFactory)
{ {
parent::__construct($httpClient, $logger); parent::__construct($httpClient, $serializer, $logger);
$this->postFactory = $postFactory; $this->postFactory = $postFactory;
} }

View file

@ -0,0 +1,183 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
use GuzzleHttp\ClientInterface;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Serializer;
use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Auth;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\User as UserDTO;
use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\ForbiddenException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\InvalidResponseException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\NotFoundException;
use Skobkin\Bundle\PointToolsBundle\Exception\Api\UserNotFoundException;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory;
/**
* Basic Point.im user API functions from /api/user/*
*/
class UserApi extends AbstractApi
{
private const PREFIX = '/api/user/';
/**
* @var UserFactory
*/
private $userFactory;
public function __construct(ClientInterface $httpClient, Serializer $serializer, LoggerInterface $logger, UserFactory $userFactory)
{
parent::__construct($httpClient, $serializer, $logger);
$this->userFactory = $userFactory;
}
public function isLoginAndPasswordValid(string $login, string $password): bool
{
$this->logger->info('Checking user auth data via point.im API');
$auth = $this->authenticate($login, $password);
if ($auth->isValid()) {
$this->logger->debug('Authentication successfull. Logging out.');
$this->logout($auth);
return true;
}
return false;
}
public function authenticate(string $login, string $password): Auth
{
$this->logger->debug('Trying to authenticate user via Point.im API', ['login' => $login]);
try {
return $this->getPostJsonData(
'/api/login',
[
'login' => $login,
'password' => $password,
],
Auth::class
);
} catch (NotFoundException $e) {
throw new InvalidResponseException('API method not found', 0, $e);
}
}
/**
* @throws InvalidResponseException
*/
public function logout(Auth $auth): bool
{
$this->logger->debug('Trying to log user out via Point.im API');
try {
$this->getPostResponseBody('/api/logout', ['csrf_token' => $auth->getCsRfToken()]);
return true;
} catch (NotFoundException $e) {
throw new InvalidResponseException('API method not found', 0, $e);
} catch (ForbiddenException $e) {
return true;
}
}
/**
* Get user subscribers by user login
*
* @return User[]
*
* @throws UserNotFoundException
*/
public function getUserSubscribersByLogin(string $login): array
{
$this->logger->debug('Trying to get user subscribers by login', ['login' => $login]);
try {
$usersList = $this->getGetJsonData(
self::PREFIX.urlencode($login).'/subscribers',
[],
'array<'.UserDTO::class.'>',
DeserializationContext::create()->setGroups(['user_short'])
);
} catch (NotFoundException $e) {
throw new UserNotFoundException('User not found', 0, $e, null, $login);
}
return $this->userFactory->findOrCreateFromDTOArray($usersList);
}
/**
* Get user subscribers by user id
*
* @return User[]
*
* @throws UserNotFoundException
*/
public function getUserSubscribersById(int $id): array
{
$this->logger->debug('Trying to get user subscribers by id', ['id' => $id]);
try {
$usersList = $this->getGetJsonData(
self::PREFIX.'id/'.$id.'/subscribers',
[],
'array<'.UserDTO::class.'>',
DeserializationContext::create()->setGroups(['user_short'])
);
} catch (NotFoundException $e) {
throw new UserNotFoundException('User not found', 0, $e, $id);
}
return $this->userFactory->findOrCreateFromDTOArray($usersList);
}
/**
* Get full user info by login
*/
public function getUserByLogin(string $login): User
{
$this->logger->debug('Trying to get user by login', ['login' => $login]);
try {
/** @var UserDTO $userInfo */
$userInfo = $this->getGetJsonData(
self::PREFIX.'login/'.urlencode($login),
[],
UserDTO::class,
DeserializationContext::create()->setGroups(['user_full'])
);
} catch (NotFoundException $e) {
throw new UserNotFoundException('User not found', 0, $e, null, $login);
}
return $this->userFactory->findOrCreateFromDTO($userInfo);
}
/**
* Get full user info by id
*/
public function getUserById(int $id): User
{
$this->logger->debug('Trying to get user by id', ['id' => $id]);
try {
/** @var UserDTO $userData */
$userData = $this->getGetJsonData(
self::PREFIX.'id/'.$id,
[],
UserDTO::class,
DeserializationContext::create()->setGroups(['user_full'])
);
} catch (NotFoundException $e) {
throw new UserNotFoundException('User not found', 0, $e, $id);
}
return $this->userFactory->findOrCreateFromDTO($userData);
}
}

View file

@ -1,9 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Exceptions;
class ApiException extends \Exception
{
}

View file

@ -1,27 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Exceptions\Factory;
use Exception;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\User;
class InvalidUserDataException extends \Exception
{
/**
* @var User
*/
private $user;
public function __construct($message = "", User $user, $code = 0, Exception $previous = null)
{
$this->user = $user;
parent::__construct($message, $code, $previous);
}
public function getUser(): User
{
return $this->user;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory;
use Psr\Log\LoggerInterface;
class AbstractFactory
{
/**
* @var LoggerInterface
*/
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}

View file

@ -2,14 +2,13 @@
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs; namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment; use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\CommentRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\CommentRepository;
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException; use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory; use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory;
class CommentFactory class CommentFactory extends AbstractFactory
{ {
/** /**
* @var CommentRepository * @var CommentRepository
@ -27,83 +26,11 @@ class CommentFactory
private $userFactory; private $userFactory;
public function __construct(CommentRepository $commentRepository, PostRepository $postRepository, UserFactory $userFactory) public function __construct(LoggerInterface $logger, CommentRepository $commentRepository, PostRepository $postRepository, UserFactory $userFactory)
{ {
parent::__construct($logger);
$this->userFactory = $userFactory; $this->userFactory = $userFactory;
$this->commentRepository = $commentRepository; $this->commentRepository = $commentRepository;
$this->postRepository = $postRepository; $this->postRepository = $postRepository;
} }
/**
* @param array $data
*
* @return Comment
*
* @throws ApiException
* @throws InvalidResponseException
*/
public function createFromArray(array $data): Comment
{
$this->validateData($data);
if (null === ($comment = $this->commentRepository->find(['post' => $data['post_id'], 'id' => $data['id']]))) {
// @fixme rare non-existing post bug
$post = $this->postRepository->find($data['post_id']);
$author = $this->userFactory->createFromArray($data['author']);
if (null !== $data['to_comment_id']) {
$toComment = $this->commentRepository->find(['post' => $data['post_id'], 'id' => $data['to_comment_id']]);
} else {
$toComment = null;
}
$createdAt = new \DateTime($data['created']);
$comment = new Comment($data['id'], $post, $author, $toComment, $data['text'], $createdAt, $data['is_rec']);
$this->commentRepository->add($comment);
}
return $comment;
}
/**
* @param array $data
*
* @return Comment[]
*
* @throws ApiException
*/
public function createFromListArray(array $data): array
{
$comments = [];
foreach ($data as $commentData) {
$comments[] = $this->createFromArray($commentData);
}
return $comments;
}
/**
* @param array $data
*
* @throws InvalidResponseException
*/
private function validateData(array $data): void
{
if (!array_key_exists('author', $data)) {
throw new InvalidResponseException('Comment author data not found in API response');
}
// Post
if (!(
array_key_exists('id', $data) &&
array_key_exists('is_rec', $data) &&
array_key_exists('to_comment_id', $data) &&
array_key_exists('post_id', $data) &&
array_key_exists('text', $data) &&
array_key_exists('created', $data)
)) {
throw new InvalidResponseException('Comment data not found in API response');
}
}
} }

View file

@ -5,24 +5,20 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\File; use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\File;
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\FileRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\FileRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException; use Skobkin\Bundle\PointToolsBundle\Exception\Api\InvalidResponseException;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
class FileFactory class FileFactory extends AbstractFactory
{ {
/**
* @var LoggerInterface
*/
private $log;
/** /**
* @var FileRepository * @var FileRepository
*/ */
private $fileRepository; private $fileRepository;
public function __construct(LoggerInterface $log, FileRepository $fileRepository) public function __construct(LoggerInterface $logger, FileRepository $fileRepository)
{ {
$this->log = $log; parent::__construct($logger);
$this->fileRepository = $fileRepository; $this->fileRepository = $fileRepository;
} }
@ -40,7 +36,7 @@ class FileFactory
$file = $this->createFromUrl($url); $file = $this->createFromUrl($url);
$files[] = $file; $files[] = $file;
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log->error('Error while creating file from DTO', ['file' => $url, 'message' => $e->getMessage()]); $this->logger->error('Error while creating file from DTO', ['file' => $url, 'message' => $e->getMessage()]);
continue; continue;
} }
} }

View file

@ -4,24 +4,19 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\MetaPost; use Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\PostsPage; use Skobkin\Bundle\PointToolsBundle\DTO\Api\PostsPage;
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post; use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\PostTag; use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\PostTag;
use Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog\InvalidDataException; use Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog\InvalidDataException;
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException;
use Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog\InvalidPostDataException; use Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog\InvalidPostDataException;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException; use Skobkin\Bundle\PointToolsBundle\Exception\Api\InvalidResponseException;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory; use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory;
class PostFactory class PostFactory extends AbstractFactory
{ {
/**
* @var LoggerInterface
*/
private $logger;
/** /**
* @var EntityManagerInterface * @var EntityManagerInterface
*/ */
@ -62,7 +57,7 @@ class PostFactory
CommentFactory $commentFactory, CommentFactory $commentFactory,
TagFactory $tagFactory TagFactory $tagFactory
) { ) {
$this->logger = $logger; parent::__construct($logger);
$this->em = $em; $this->em = $em;
$this->postRepository = $postRepository; $this->postRepository = $postRepository;
$this->userFactory = $userFactory; $this->userFactory = $userFactory;
@ -78,7 +73,6 @@ class PostFactory
* *
* @return bool * @return bool
* *
* @throws ApiException
* @throws InvalidResponseException * @throws InvalidResponseException
*/ */
public function createFromPageDTO(PostsPage $page): bool public function createFromPageDTO(PostsPage $page): bool
@ -122,7 +116,6 @@ class PostFactory
* *
* @return Post * @return Post
* *
* @throws ApiException
* @throws InvalidDataException * @throws InvalidDataException
*/ */
private function createFromDTO(MetaPost $postData): Post private function createFromDTO(MetaPost $postData): Post
@ -137,7 +130,7 @@ class PostFactory
} }
try { try {
$user = $this->userFactory->createFromDTO($postData->getPost()->getAuthor()); $user = $this->userFactory->findOrCreateFromDTO($postData->getPost()->getAuthor());
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('Error while creating user from DTO'); $this->logger->error('Error while creating user from DTO');
throw $e; throw $e;

View file

@ -5,14 +5,10 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Tag; use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Tag;
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\TagRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\TagRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
class TagFactory class TagFactory extends AbstractFactory
{ {
/**
* @var LoggerInterface
*/
private $logger;
/** /**
* @var TagRepository * @var TagRepository
*/ */
@ -21,7 +17,7 @@ class TagFactory
public function __construct(LoggerInterface $logger, TagRepository $tagRepository) public function __construct(LoggerInterface $logger, TagRepository $tagRepository)
{ {
$this->logger = $logger; parent::__construct($logger);
$this->tagRepository = $tagRepository; $this->tagRepository = $tagRepository;
} }

View file

@ -2,11 +2,13 @@
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram; namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram;
use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\Entity\Telegram\Account; use Skobkin\Bundle\PointToolsBundle\Entity\Telegram\Account;
use Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
use unreal4u\TelegramAPI\Telegram\Types\Message; use unreal4u\TelegramAPI\Telegram\Types\Message;
class AccountFactory class AccountFactory extends AbstractFactory
{ {
/** /**
* @var AccountRepository * @var AccountRepository
@ -14,8 +16,9 @@ class AccountFactory
private $accountRepo; private $accountRepo;
public function __construct(AccountRepository $accountRepository) public function __construct(LoggerInterface $logger, AccountRepository $accountRepository)
{ {
parent::__construct($logger);
$this->accountRepo = $accountRepository; $this->accountRepo = $accountRepository;
} }

View file

@ -2,67 +2,40 @@
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory; namespace Skobkin\Bundle\PointToolsBundle\Service\Factory;
use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Crawler\User as UserDTO; use Skobkin\Bundle\PointToolsBundle\DTO\Api\User as UserDTO;
use Skobkin\Bundle\PointToolsBundle\Entity\User; use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository; use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException; use Skobkin\Bundle\PointToolsBundle\Exception\Factory\InvalidUserDataException;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\Factory\InvalidUserDataException;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException;
class UserFactory class UserFactory extends AbstractFactory
{ {
/** /**
* @var UserRepository * @var UserRepository
*/ */
private $userRepository; private $userRepository;
/**
* @param EntityManagerInterface $em public function __construct(LoggerInterface $logger, UserRepository $userRepository)
*/
public function __construct(UserRepository $userRepository)
{ {
parent::__construct($logger);
$this->userRepository = $userRepository; $this->userRepository = $userRepository;
} }
/**
* @param array $data
*
* @return User
*
* @throws InvalidResponseException
*/
public function createFromArray(array $data): User
{
$this->validateArrayData($data);
/** @var User $user */
if (null === ($user = $this->userRepository->find($data['id']))) {
// Creating new user
$user = new User($data['id']);
$this->userRepository->add($user);
}
// Updating data
$user
->setLogin($data['login'])
->setName($data['name'])
;
return $user;
}
/** /**
* @param UserDTO $userData * @param UserDTO $userData
* *
* @return User * @return User
* *
* @throws ApiException
* @throws InvalidUserDataException * @throws InvalidUserDataException
*/ */
public function createFromDTO(UserDTO $userData): User public function findOrCreateFromDTO(UserDTO $userData): User
{ {
$this->validateDTOData($userData); // @todo LOG
if (!$userData->isValid()) {
throw new InvalidUserDataException('Invalid user data', $userData);
}
/** @var User $user */ /** @var User $user */
if (null === ($user = $this->userRepository->find($userData->getId()))) { if (null === ($user = $this->userRepository->find($userData->getId()))) {
@ -80,27 +53,16 @@ class UserFactory
return $user; return $user;
} }
/** public function findOrCreateFromDTOArray(array $usersData): array
* @param array $data
*
* @throws InvalidResponseException
*/
private function validateArrayData(array $data): void
{ {
if (!array_key_exists('id', $data) || !array_key_exists('login', $data) || !array_key_exists('name', $data) || !is_numeric($data['id'])) { // @todo LOG
throw new InvalidResponseException('Invalid user data');
} $result = [];
foreach ($usersData as $userData) {
$result[] = $this->findOrCreateFromDTO($userData);
} }
/** return $result;
* @param UserDTO $data
*
* @throws InvalidResponseException
*/
private function validateDTOData(UserDTO $data): void
{
if (!$data->getId() || !$data->getLogin()) {
throw new InvalidUserDataException('User have no id or login', $data);
}
} }
} }

View file

@ -11,7 +11,7 @@ use Skobkin\Bundle\PointToolsBundle\Repository\SubscriptionRepository;
use Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository; use Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository;
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository; use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory; use Skobkin\Bundle\PointToolsBundle\Service\Factory\Telegram\AccountFactory;
use Skobkin\Bundle\PointToolsBundle\Service\UserApi; use Skobkin\Bundle\PointToolsBundle\Service\Api\UserApi;
use unreal4u\TelegramAPI\Telegram\Types\Message; use unreal4u\TelegramAPI\Telegram\Types\Message;
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardMarkup; use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardMarkup;
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardRemove; use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardRemove;
@ -174,7 +174,7 @@ class PrivateMessageProcessor
throw new CommandProcessingException('User not found in Point Tools database. Please try again later.'); throw new CommandProcessingException('User not found in Point Tools database. Please try again later.');
} }
if ($this->userApi->isAuthDataValid($login, $password)) { if ($this->userApi->isLoginAndPasswordValid($login, $password)) {
$account->setUser($user); $account->setUser($user);
return true; return true;

View file

@ -1,311 +0,0 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Service;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
use JMS\Serializer\Serializer;
use Psr\Log\LoggerInterface;
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Auth;
use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException;
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\UserNotFoundException;
use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory;
use Symfony\Component\HttpFoundation\Response;
/**
* Basic Point.im user API functions from /api/user/*
*/
class UserApi extends AbstractApi
{
/**
* @var UserFactory
*/
private $userFactory;
/**
* @var Serializer
*/
private $serializer;
public function __construct(ClientInterface $httpClient, LoggerInterface $logger, UserFactory $userFactory, Serializer $serializer)
{
parent::__construct($httpClient, $logger);
$this->userFactory = $userFactory;
$this->serializer = $serializer;
}
public function isAuthDataValid(string $login, string $password): bool
{
$this->logger->info('Checking user auth data via point.im API');
$auth = $this->authenticate($login, $password);
if (null === $auth->getError() && null !== $auth->getToken()) {
$this->logger->debug('Authentication successfull. Logging out.');
$this->logout($auth);
return true;
}
return false;
}
public function authenticate(string $login, string $password): Auth
{
$this->logger->debug('Trying to authenticate user via Point.im API', ['login' => $login]);
try {
$authData = $this->getPostRequestData(
'/api/login',
[
'login' => $login,
'password' => $password,
]
);
return $this->serializer->deserialize($authData, Auth::class, 'json');
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new InvalidResponseException('API method not found', 0, $e);
} else {
throw $e;
}
}
}
public function logout(Auth $auth): bool
{
$this->logger->debug('Trying to log user out via Point.im API');
try {
$this->getPostRequestData('/api/logout', ['csrf_token' => $auth->getCsRfToken()]);
return true;
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new InvalidResponseException('API method not found', 0, $e);
} elseif (Response::HTTP_FORBIDDEN === $e->getResponse()->getStatusCode()) {
return true;
} else {
throw $e;
}
}
}
/**
* Get user subscribers by user login
*
* @param string $login
*
* @return User[]
*
* @throws ApiException
* @throws InvalidResponseException
* @throws UserNotFoundException
*/
public function getUserSubscribersByLogin(string $login): array
{
$this->logger->debug('Trying to get user subscribers by login', ['login' => $login]);
try {
$usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscribers', [], true);
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new UserNotFoundException('User not found', 0, $e, null, $login);
} else {
throw $e;
}
}
return $this->getUsersFromList($usersList);
}
/**
* Get user subscribers by user id
*
* @param int $id
*
* @return User[]
*
* @throws ApiException
* @throws InvalidResponseException
* @throws UserNotFoundException
*/
public function getUserSubscribersById(int $id): array
{
$this->logger->debug('Trying to get user subscribers by id', ['id' => $id]);
try {
$usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscribers', [], true);
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new UserNotFoundException('User not found', 0, $e, $id);
} else {
throw $e;
}
}
return $this->getUsersFromList($usersList);
}
/**
* Get user subscriptions by user login
*
* @param string $login
*
* @return User[]
*
* @throws ApiException
* @throws InvalidResponseException
* @throws UserNotFoundException
*/
public function getUserSubscriptionsByLogin(string $login): array
{
$this->logger->debug('Trying to get user subscriptions by login', ['login' => $login]);
try {
$usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscriptions', [], true);
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new UserNotFoundException('User not found', 0, $e, null, $login);
} else {
throw $e;
}
}
return $this->getUsersFromList($usersList);
}
/**
* Get user subscriptions by user id
*
* @param int $id
*
* @return User[]
*
* @throws ApiException
* @throws InvalidResponseException
* @throws UserNotFoundException
*/
public function getUserSubscriptionsById(int $id): array
{
$this->logger->debug('Trying to get user subscriptions by id', ['id' => $id]);
try {
$usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscriptions', [], true);
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new UserNotFoundException('User not found', 0, $e, $id);
} else {
throw $e;
}
}
return $this->getUsersFromList($usersList);
}
/**
* Get single user by login
*
* @param string $login
*
* @return User
*
* @throws UserNotFoundException
* @throws RequestException
*/
public function getUserByLogin(string $login): User
{
$this->logger->debug('Trying to get user by login', ['login' => $login]);
try {
$userInfo = $this->getGetRequestData('/api/user/login/'.urlencode($login), [], true);
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new UserNotFoundException('User not found', 0, $e, null, $login);
} else {
throw $e;
}
}
return $this->getUserFromUserInfo($userInfo);
}
/**
* Get single user by id
*
* @param int $id
*
* @return User
*
* @throws UserNotFoundException
* @throws RequestException
*/
public function getUserById(int $id): User
{
$this->logger->debug('Trying to get user by id', ['id' => $id]);
try {
$userInfo = $this->getGetRequestData('/api/user/id/'.$id, [], true);
} catch (RequestException $e) {
if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
throw new UserNotFoundException('User not found', 0, $e, $id);
} else {
throw $e;
}
}
return $this->getUserFromUserInfo($userInfo);
}
/**
* Finds and updates or create new user from API response data
*
* @param array $userInfo
*
* @return User
*
* @throws ApiException
* @throws InvalidResponseException
*/
private function getUserFromUserInfo(array $userInfo): User
{
$this->logger->debug('Trying to create user from array', ['array' => $userInfo]);
return $this->userFactory->createFromArray($userInfo);
}
/**
* Get array of User objects from API response containing user list
*
* @param array $users
*
* @return User[]
*
* @throws ApiException
* @throws InvalidResponseException
*/
private function getUsersFromList(array $users = []): array
{
$this->logger->debug('Trying to create multiple users from array', ['array' => $users]);
if (array_key_exists('error', $users)) {
$this->logger->error('User list contains error object', ['error' => $users['error']]);
throw new ApiException('User list response contains error object');
}
/** @var User[] $resultUsers */
$resultUsers = [];
foreach ($users as $userInfo) {
$resultUsers[] = $this->getUserFromUserInfo($userInfo);
}
return $resultUsers;
}
}