Compare commits
14 commits
feature_po
...
master
Author | SHA1 | Date | |
---|---|---|---|
07f89712d9 | |||
fd09f389f7 | |||
b6f7ac8ec5 | |||
f598864d4d | |||
44c4158602 | |||
aa751bbbc1 | |||
9e5f59a2b2 | |||
7fcdcbf728 | |||
60dcc5e955 | |||
c3605b2db1 | |||
b455a6c8e7 | |||
5e8935ce66 | |||
d9c0673445 | |||
0c004085fd |
|
@ -19,7 +19,6 @@ class AppKernel extends Kernel
|
|||
new Symfony\Bundle\WebServerBundle\WebServerBundle(),
|
||||
new JMS\SerializerBundle\JMSSerializerBundle(),
|
||||
new Csa\Bundle\GuzzleBundle\CsaGuzzleBundle(),
|
||||
new Leezy\PheanstalkBundle\LeezyPheanstalkBundle(),
|
||||
new Ob\HighchartsBundle\ObHighchartsBundle(),
|
||||
new Knp\Bundle\MarkdownBundle\KnpMarkdownBundle(),
|
||||
new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Issue #44 - Post and Comment schema refactoring.
|
||||
* - Post subscription status added.
|
||||
* - Comments parent-child relations removed.
|
||||
* - User login index
|
||||
* - Other adjustments
|
||||
*/
|
||||
class Version20180427143940 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE posts.posts ADD is_subscribed BOOLEAN DEFAULT FALSE NOT NULL');
|
||||
// Removing parent_id constraint and index
|
||||
$this->addSql('ALTER TABLE posts.comments DROP CONSTRAINT fk_62899975727aca70');
|
||||
$this->addSql('DROP INDEX posts.idx_62899975727aca70');
|
||||
$this->addSql('ALTER TABLE posts.comments DROP parent_id');
|
||||
|
||||
$this->addSql('ALTER TABLE posts.comments ADD to_number INT');
|
||||
$this->addSql('ALTER TABLE posts.comments ALTER number TYPE INT');
|
||||
$this->addSql('ALTER TABLE posts.comments ALTER number DROP DEFAULT');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_6289997596901F54 ON posts.comments (number)');
|
||||
|
||||
$this->addSql('CREATE INDEX idx_user_login ON users.users (login)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE posts.posts DROP is_subscribed');
|
||||
$this->addSql('DROP INDEX posts.UNIQ_6289997596901F54');
|
||||
$this->addSql('ALTER TABLE posts.comments ADD parent_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE posts.comments DROP to_number');
|
||||
$this->addSql('ALTER TABLE posts.comments ALTER number TYPE SMALLINT');
|
||||
$this->addSql('ALTER TABLE posts.comments ALTER number DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE posts.comments ADD CONSTRAINT fk_62899975727aca70 FOREIGN KEY (parent_id) REFERENCES posts.comments (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX idx_62899975727aca70 ON posts.comments (parent_id)');
|
||||
$this->addSql('DROP INDEX users.idx_user_login');
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20190227233244 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema) : void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('CREATE UNIQUE INDEX unique_post_id_comment_number ON posts.comments (post_id, number)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP INDEX posts.unique_post_id_comment_number');
|
||||
}
|
||||
}
|
|
@ -71,13 +71,6 @@ doctrine_migrations:
|
|||
table_name: migration_versions
|
||||
name: Application Migrations
|
||||
|
||||
leezy_pheanstalk:
|
||||
pheanstalks:
|
||||
primary:
|
||||
server: "%beanstalkd_host%"
|
||||
port: "%beanstalkd_port%"
|
||||
default: true
|
||||
|
||||
# Swiftmailer Configuration
|
||||
swiftmailer:
|
||||
transport: "%mailer_transport%"
|
||||
|
|
|
@ -6,11 +6,6 @@ parameters:
|
|||
database_user: point
|
||||
database_password: ~
|
||||
|
||||
# Message Queue settings
|
||||
beanstalkd_host: 'localhost'
|
||||
beanstalkd_port: 11300
|
||||
beanstalkd_ws_updates_tube: 'point-websocket-updates'
|
||||
|
||||
mailer_transport: smtp
|
||||
mailer_host: 127.0.0.1
|
||||
mailer_user: ~
|
||||
|
|
|
@ -9,4 +9,4 @@ security:
|
|||
security: false
|
||||
|
||||
default:
|
||||
anonymous: ~
|
||||
anonymous: true
|
||||
|
|
|
@ -101,13 +101,6 @@ services:
|
|||
# Send message
|
||||
Skobkin\Bundle\PointToolsBundle\Command\TelegramSendMessageCommand:
|
||||
tags: [{ name: console.command }]
|
||||
# WebSocket MQ processing
|
||||
Skobkin\Bundle\PointToolsBundle\Command\ProcessWebsocketUpdatesCommand:
|
||||
arguments:
|
||||
$bsClient: '@leezy.pheanstalk.primary'
|
||||
$bsTubeName: '%beanstalkd_ws_updates_tube%'
|
||||
tags:
|
||||
- { name: console.command }
|
||||
|
||||
|
||||
# Entity repositories as services
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
},
|
||||
"require": {
|
||||
"php": ">=7.1.0",
|
||||
"ext-json": "*",
|
||||
"symfony/symfony": "^3.4",
|
||||
"doctrine/orm": "^2.5",
|
||||
"doctrine/annotations": "^1.3.0",
|
||||
|
@ -33,8 +34,7 @@
|
|||
"unreal4u/telegram-api": "^2.2",
|
||||
"csa/guzzle-bundle": "^3",
|
||||
"symfony/web-server-bundle": "^3.3",
|
||||
"sentry/sentry-symfony": "^2.2",
|
||||
"leezy/pheanstalk-bundle": "^3.3"
|
||||
"sentry/sentry-symfony": "^2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^3.0",
|
||||
|
|
1912
composer.lock
generated
1912
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Command;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use JMS\Serializer\Serializer;
|
||||
use Leezy\PheanstalkBundle\Proxy\PheanstalkProxy;
|
||||
use Pheanstalk\Job;
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message;
|
||||
use Skobkin\Bundle\PointToolsBundle\Exception\WebSocket\UnsupportedTypeException;
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\WebSocket\WebSocketMessageProcessor;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\{InputInterface, InputOption};
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* This command processes WebSocket updates MQ and stores new content in the DB
|
||||
*/
|
||||
class ProcessWebsocketUpdatesCommand extends Command
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
/** @var PheanstalkProxy */
|
||||
private $bsClient;
|
||||
|
||||
/** @var string */
|
||||
private $bsTubeName;
|
||||
|
||||
/** @var Serializer */
|
||||
private $serializer;
|
||||
|
||||
/** @var WebSocketMessageProcessor */
|
||||
private $messageProcessor;
|
||||
|
||||
/** @var \Raven_Client */
|
||||
private $sentryClient;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
\Raven_Client $raven,
|
||||
PheanstalkProxy $bsClient,
|
||||
string $bsTubeName,
|
||||
Serializer $serializer,
|
||||
WebSocketMessageProcessor $processor
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->sentryClient = $raven;
|
||||
$this->serializer = $serializer;
|
||||
$this->messageProcessor = $processor;
|
||||
$this->bsClient = $bsClient;
|
||||
$this->bsTubeName = $bsTubeName;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritdoc} */
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('point:update:websocket-messages')
|
||||
->setDescription('Reads and processes updates from Beanstalkd queue pipe')
|
||||
->addOption('keep-jobs', 'k', InputOption::VALUE_NONE, 'Don\'t delete jobs from queue after processing')
|
||||
;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$keepJobs = (bool) $input->getOption('keep-jobs');
|
||||
|
||||
/** @var Job $job */
|
||||
while ($job = $this->bsClient->reserveFromTube($this->bsTubeName, 0)) {
|
||||
try {
|
||||
/** @var Message $message */
|
||||
$message = $this->serializer->deserialize($job->getData(), Message::class, 'json');
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln(sprintf(
|
||||
'Error while deserializing #%d data: \'%s\'',
|
||||
$job->getId(),
|
||||
$job->getData()
|
||||
));
|
||||
$this->sentryClient->captureException($e);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$output->writeln('Processing job #'.$job->getId().' ('.$message->getA().')');
|
||||
|
||||
try {
|
||||
if ($this->messageProcessor->processMessage($message)) {
|
||||
$this->em->flush();
|
||||
|
||||
if (!$keepJobs) {
|
||||
$this->bsClient->delete($job);
|
||||
}
|
||||
|
||||
$this->em->clear();
|
||||
}
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
$output->writeln(' Unsupported message type: '.$message->getA());
|
||||
$this->sentryClient->captureException($e);
|
||||
|
||||
continue;
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln(' Message processing error: '.$e->getMessage());
|
||||
$this->sentryClient->captureException($e);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,11 +12,19 @@ class PostController extends AbstractController
|
|||
{
|
||||
/**
|
||||
* @ParamConverter("post", class="SkobkinPointToolsBundle:Blogs\Post")
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function showAction(Post $post, PostRepository $postRepository): Response
|
||||
{
|
||||
if ((!$post->getAuthor()->isPublic()) || $post->getAuthor()->isWhitelistOnly()) {
|
||||
/**
|
||||
* Throwing 404 instead of 403 because of
|
||||
* @see \Symfony\Component\Security\Http\Firewall\ExceptionListener::handleAccessDeniedException()
|
||||
* starts to replace 403 by 401 exceptions for anonymous users and tries to authenticate them.
|
||||
*/
|
||||
throw $this->createNotFoundException('Author\'s blog is private.');
|
||||
//throw $this->createAccessDeniedException('Author\'s blog is private.');
|
||||
}
|
||||
|
||||
return $this->render('SkobkinPointToolsBundle:Post:show.html.twig', [
|
||||
'post' => $postRepository->getPostWithComments($post->getId()),
|
||||
]);
|
||||
|
|
|
@ -4,22 +4,39 @@ namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
|
|||
|
||||
class Comment implements ValidableInterface
|
||||
{
|
||||
/** @var int|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $postId;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $number;
|
||||
|
||||
/** @var int|null */
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $toCommentId;
|
||||
|
||||
/** @var string|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/** @var string|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $text;
|
||||
|
||||
/** @var User|null */
|
||||
/**
|
||||
* @var User|null
|
||||
*/
|
||||
private $author;
|
||||
|
||||
/** @var bool|null */
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private $isRec;
|
||||
|
||||
|
||||
|
|
|
@ -4,17 +4,27 @@ namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
|
|||
|
||||
class MetaPost implements ValidableInterface
|
||||
{
|
||||
/** @var Post|null */
|
||||
/**
|
||||
* @var Post|null
|
||||
*/
|
||||
private $post;
|
||||
|
||||
/** @var Comment[]|null */
|
||||
/**
|
||||
* @var Comment[]|null
|
||||
*/
|
||||
private $comments;
|
||||
|
||||
|
||||
public function getPost(): ?Post
|
||||
{
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
public function setPost(?Post $post): void
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Comment[]|null
|
||||
*/
|
||||
|
@ -23,8 +33,20 @@ class MetaPost implements ValidableInterface
|
|||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Comment[]|null $comments
|
||||
*/
|
||||
public function setComments(?array $comments): void
|
||||
{
|
||||
$this->comments = $comments;
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return (null !== $this->post && $this->post->isValid());
|
||||
if (null !== $this->post && $this->post->isValid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -4,28 +4,44 @@ namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
|
|||
|
||||
class Post implements ValidableInterface
|
||||
{
|
||||
/** @var string|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/** @var string[]|null */
|
||||
/**
|
||||
* @var string[]|null
|
||||
*/
|
||||
private $tags;
|
||||
|
||||
/** @var string[]|null */
|
||||
/**
|
||||
* @var string[]|null
|
||||
*/
|
||||
private $files;
|
||||
|
||||
/** @var User|null */
|
||||
/**
|
||||
* @var User|null
|
||||
*/
|
||||
private $author;
|
||||
|
||||
/** @var string|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $text;
|
||||
|
||||
/** @var string|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/** @var string|null */
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/** @var bool|null */
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private $private;
|
||||
|
||||
|
||||
|
|
|
@ -1,330 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket;
|
||||
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\ValidableInterface;
|
||||
use Skobkin\Bundle\PointToolsBundle\Exception\WebSocket\UnsupportedTypeException;
|
||||
|
||||
/**
|
||||
* WebSocket update message
|
||||
*/
|
||||
class Message implements ValidableInterface
|
||||
{
|
||||
public const TYPE_COMMENT = 'comment';
|
||||
public const TYPE_COMMENT_EDITED = 'comment_edited';
|
||||
public const TYPE_POST = 'post';
|
||||
public const TYPE_POST_EDITED = 'post_edited';
|
||||
public const TYPE_POST_COMMENT_RECOMMENDATION = 'rec';
|
||||
public const TYPE_RECOMMENDATION_WITH_COMMENT = 'ok';
|
||||
|
||||
/**
|
||||
* Event type. @see Message::TYPE_* constants
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $a;
|
||||
|
||||
/**
|
||||
* Login of the user
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $author;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $authorId;
|
||||
|
||||
/** @var string|null */
|
||||
private $authorName;
|
||||
|
||||
/**
|
||||
* Number of the comment in the thread
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $commentId;
|
||||
|
||||
/**
|
||||
* ???
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
private $cut;
|
||||
|
||||
/**
|
||||
* Array of file paths
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
private $files;
|
||||
|
||||
/** @var string|null */
|
||||
private $html;
|
||||
|
||||
/**
|
||||
* @deprecated Link in the Post::type=feed posts
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $link;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $postAuthorId;
|
||||
|
||||
/** @var string */
|
||||
private $postId;
|
||||
|
||||
/** @var bool|null */
|
||||
private $private;
|
||||
|
||||
/**
|
||||
* Number of the comment in the thread for recommendation with text
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $rcid;
|
||||
|
||||
/**
|
||||
* Array of tags
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
private $tags;
|
||||
|
||||
/** @var string */
|
||||
private $text;
|
||||
|
||||
/**
|
||||
* @deprecated ???
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* Number of the comment to which this comment is answering
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $toCommentId;
|
||||
|
||||
/**
|
||||
* Text quotation of the comment to which this comment is answering
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $toText;
|
||||
|
||||
/**
|
||||
* Array of logins of users to which post is addressed
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
private $toUsers;
|
||||
|
||||
public function isPost(): bool
|
||||
{
|
||||
return self::TYPE_POST === $this->a;
|
||||
}
|
||||
|
||||
public function isComment(): bool
|
||||
{
|
||||
return self::TYPE_COMMENT === $this->a;
|
||||
}
|
||||
|
||||
public function isCommentRecommendation(): bool
|
||||
{
|
||||
return self::TYPE_RECOMMENDATION_WITH_COMMENT === $this->a;
|
||||
}
|
||||
|
||||
public function isPostRecommendation(): bool
|
||||
{
|
||||
return self::TYPE_POST_COMMENT_RECOMMENDATION === $this->a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException
|
||||
* @throws UnsupportedTypeException
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
switch ($this->a) {
|
||||
case self::TYPE_POST:
|
||||
return $this->isValidPost();
|
||||
break;
|
||||
|
||||
case self::TYPE_POST_EDITED:
|
||||
return $this->isValidPostEdited();
|
||||
break;
|
||||
|
||||
case self::TYPE_COMMENT;
|
||||
return $this->isValidComment();
|
||||
break;
|
||||
|
||||
case self::TYPE_COMMENT_EDITED;
|
||||
return $this->isValidCommentEdited();
|
||||
break;
|
||||
|
||||
case self::TYPE_RECOMMENDATION_WITH_COMMENT;
|
||||
return $this->isValidRecommendationWithComment();
|
||||
break;
|
||||
|
||||
case self::TYPE_POST_COMMENT_RECOMMENDATION;
|
||||
return $this->isValidPostCommentRecommendation();
|
||||
break;
|
||||
|
||||
case null:
|
||||
throw new \RuntimeException('Message has NULL type.');
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedTypeException(sprintf('Type \'%s\' is not supported.', $this->a));
|
||||
}
|
||||
}
|
||||
|
||||
public function getA(): string
|
||||
{
|
||||
return $this->a;
|
||||
}
|
||||
|
||||
public function getAuthor(): string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function getAuthorId(): int
|
||||
{
|
||||
return $this->authorId;
|
||||
}
|
||||
|
||||
public function getAuthorName(): ?string
|
||||
{
|
||||
return $this->authorName;
|
||||
}
|
||||
|
||||
public function getCommentId(): ?int
|
||||
{
|
||||
return $this->commentId;
|
||||
}
|
||||
|
||||
public function getCut(): ?bool
|
||||
{
|
||||
return $this->cut;
|
||||
}
|
||||
|
||||
public function getFiles(): ?array
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
public function getHtml(): ?string
|
||||
{
|
||||
return $this->html;
|
||||
}
|
||||
|
||||
public function getLink(): ?string
|
||||
{
|
||||
return $this->link;
|
||||
}
|
||||
|
||||
public function getPostId(): string
|
||||
{
|
||||
return $this->postId;
|
||||
}
|
||||
|
||||
public function getPostAuthorId(): ?int
|
||||
{
|
||||
return $this->postAuthorId;
|
||||
}
|
||||
|
||||
public function getPrivate(): ?bool
|
||||
{
|
||||
return $this->private;
|
||||
}
|
||||
|
||||
public function getRcid(): ?int
|
||||
{
|
||||
return $this->rcid;
|
||||
}
|
||||
|
||||
public function getTags(): ?array
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
public function getText(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getToCommentId(): ?string
|
||||
{
|
||||
return $this->toCommentId;
|
||||
}
|
||||
|
||||
public function getToText(): ?string
|
||||
{
|
||||
return $this->toText;
|
||||
}
|
||||
|
||||
public function getToUsers(): ?array
|
||||
{
|
||||
return $this->toUsers;
|
||||
}
|
||||
|
||||
private function hasCommonMandatoryData(): bool
|
||||
{
|
||||
return (
|
||||
null !== $this->author &&
|
||||
null !== $this->authorId &&
|
||||
null !== $this->postId
|
||||
);
|
||||
}
|
||||
|
||||
private function isValidPost(): bool
|
||||
{
|
||||
return $this->hasCommonMandatoryData() && (
|
||||
// Text can be empty ("") though
|
||||
null !== $this->text &&
|
||||
null !== $this->tags
|
||||
);
|
||||
}
|
||||
|
||||
private function isValidPostEdited(): bool
|
||||
{
|
||||
return $this->isValidPost();
|
||||
}
|
||||
|
||||
private function isValidComment(): bool
|
||||
{
|
||||
return $this->hasCommonMandatoryData() && (
|
||||
null !== $this->commentId &&
|
||||
null !== $this->html &&
|
||||
null !== $this->text
|
||||
);
|
||||
}
|
||||
|
||||
private function isValidCommentEdited(): bool
|
||||
{
|
||||
return $this->hasCommonMandatoryData() && (null !== $this->commentId);
|
||||
}
|
||||
|
||||
private function isValidRecommendationWithComment(): bool
|
||||
{
|
||||
return $this->hasCommonMandatoryData();
|
||||
}
|
||||
|
||||
private function isValidPostCommentRecommendation(): bool
|
||||
{
|
||||
return $this->hasCommonMandatoryData() && (null !== $this->postAuthorId);
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
|||
|
||||
class LoadCommentsData extends AbstractFixture implements OrderedFixtureInterface
|
||||
{
|
||||
public function load(ObjectManager $om): void
|
||||
public function load(ObjectManager $om)
|
||||
{
|
||||
/** @var Post $post */
|
||||
$post = $this->getReference('test_post_longpost');
|
||||
|
@ -25,28 +25,29 @@ class LoadCommentsData extends AbstractFixture implements OrderedFixtureInterfac
|
|||
$this->getReference('test_user_99995'),
|
||||
];
|
||||
|
||||
$text = 'Some text with [link to @skobkin-ru site](https://skobk.in/) and `code block`'.PHP_EOL.
|
||||
'and some quotation:'.PHP_EOL.
|
||||
'> test test quote'.PHP_EOL.
|
||||
'and some text after';
|
||||
$comments = [];
|
||||
|
||||
foreach (range(1, 10000) as $num) {
|
||||
$comment = new Comment(
|
||||
$text,
|
||||
new \DateTime(),
|
||||
false,
|
||||
$post,
|
||||
$num,
|
||||
($num > 1 && !random_int(0, 4)) ? random_int(1, $num - 1) : null,
|
||||
$users[array_rand($users)],
|
||||
[]
|
||||
);
|
||||
$comment = (new Comment())
|
||||
->setNumber($num)
|
||||
->setDeleted(mt_rand(0, 15) ? false : true)
|
||||
->setCreatedAt(new \DateTime())
|
||||
->setAuthor($users[array_rand($users)])
|
||||
->setRec(false)
|
||||
->setText(
|
||||
'Some text with [link to @skobkin-ru site](https://skobk.in/) and `code block`'.PHP_EOL.
|
||||
'and some quotation:'.PHP_EOL.
|
||||
'> test test quote'.PHP_EOL.
|
||||
'and some text after'
|
||||
)
|
||||
;
|
||||
|
||||
if (!random_int(0, 15)) {
|
||||
$comment->delete();
|
||||
if (count($comments) > 0 && mt_rand(0, 1)) {
|
||||
$comment->setParent($comments[mt_rand(0, count($comments) - 1)]);
|
||||
}
|
||||
|
||||
$post->addComment($comment);
|
||||
$comments[] = $comment;
|
||||
|
||||
$om->persist($comment);
|
||||
}
|
||||
|
|
|
@ -2,33 +2,64 @@
|
|||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\DataFixtures\ORM;
|
||||
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Common\DataFixtures\{AbstractFixture, OrderedFixtureInterface};
|
||||
use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\{Blogs\Post, User};
|
||||
|
||||
class LoadPostData extends AbstractFixture implements OrderedFixtureInterface
|
||||
{
|
||||
public const POST_ID_LONG = 'longpost';
|
||||
public const POST_ID_SHORT = 'shortpost';
|
||||
public const POST_ID_PR_USER = 'prusrpst';
|
||||
public const POST_ID_WL_USER = 'wlusrpst';
|
||||
public const POST_ID_PR_WL_USER = 'prwlusrpst';
|
||||
|
||||
public function load(ObjectManager $om)
|
||||
{
|
||||
/** @var User $testUser */
|
||||
$testUser = $this->getReference('test_user_99999');
|
||||
/** @var User $mainUser */
|
||||
$mainUser = $this->getReference('test_user_'.LoadUserData::USER_MAIN_ID);
|
||||
/** @var User $privateUser */
|
||||
$privateUser = $this->getReference('test_user_'.LoadUserData::USER_PRIV_ID);
|
||||
/** @var User $wlUser */
|
||||
$wlUser = $this->getReference('test_user_'.LoadUserData::USER_WLON_ID);
|
||||
/** @var User $prWlUser */
|
||||
$prWlUser = $this->getReference('test_user_'.LoadUserData::USER_PRWL_ID);
|
||||
|
||||
$longPost = (new Post('longpost', $testUser, new \DateTime(), Post::TYPE_POST))
|
||||
$longPost = (new Post(self::POST_ID_LONG, $mainUser, new \DateTime(), Post::TYPE_POST))
|
||||
->setText('Test post with many comments')
|
||||
->setPrivate(false)
|
||||
->setDeleted(false)
|
||||
;
|
||||
|
||||
$shortPost = (new Post('shortpost', $testUser, new \DateTime(), Post::TYPE_POST))
|
||||
$shortPost = (new Post(self::POST_ID_SHORT, $mainUser, new \DateTime(), Post::TYPE_POST))
|
||||
->setText('Test short post')
|
||||
->setPrivate(false)
|
||||
->setDeleted(false)
|
||||
;
|
||||
|
||||
$privateUserPost = (new Post(self::POST_ID_PR_USER, $privateUser, new \DateTime(), Post::TYPE_POST))
|
||||
->setText('Post from private user. Should not be visible in the public feed.')
|
||||
->setPrivate(false)
|
||||
->setDeleted(false)
|
||||
;
|
||||
|
||||
$wlUserPost = (new Post(self::POST_ID_WL_USER, $wlUser, new \DateTime(), Post::TYPE_POST))
|
||||
->setText('Post from whitelist-only user. Should only be visible for whitelisted users.')
|
||||
->setPrivate(false)
|
||||
->setDeleted(false)
|
||||
;
|
||||
|
||||
$privateWlUserPost = (new Post(self::POST_ID_PR_WL_USER, $prWlUser, new \DateTime(), Post::TYPE_POST))
|
||||
->setText('Post from private AND whitelist-only user. Should not be visible in the public feed.')
|
||||
->setPrivate(false)
|
||||
->setDeleted(false)
|
||||
;
|
||||
|
||||
$om->persist($longPost);
|
||||
$om->persist($shortPost);
|
||||
$om->persist($privateUserPost);
|
||||
$om->persist($wlUserPost);
|
||||
$om->persist($privateWlUserPost);
|
||||
$om->flush();
|
||||
|
||||
$this->addReference('test_post_longpost', $longPost);
|
||||
|
|
|
@ -9,25 +9,27 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
|||
|
||||
class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
|
||||
{
|
||||
public const USER_MAIN_ID = 99999;
|
||||
public const USER_SCND_ID = 99998;
|
||||
public const USER_PRIV_ID = 99997;
|
||||
public const USER_WLON_ID = 99996;
|
||||
public const USER_PRWL_ID = 99995;
|
||||
public const USER_UNNM_ID = 99994;
|
||||
|
||||
private $users = [
|
||||
// 99999
|
||||
['login' => 'testuser', 'name' => 'Test User 1'],
|
||||
// 99998
|
||||
['login' => 'testuser2', 'name' => 'Test User 2'],
|
||||
// 99997
|
||||
['login' => 'testuser3', 'name' => 'Test User 3'],
|
||||
// 99996
|
||||
['login' => 'testuser4', 'name' => 'Test User 4'],
|
||||
//99995
|
||||
['login' => 'testuser5', 'name' => null],
|
||||
['id' => self::USER_MAIN_ID, 'login' => 'testuser', 'name' => 'Test User 1', 'private' => false, 'whitelist-only' => false],
|
||||
['id' => self::USER_SCND_ID, 'login' => 'testuser2', 'name' => 'Test User 2 for autocomplete test', 'private' => false, 'whitelist-only' => false],
|
||||
['id' => self::USER_PRIV_ID, 'login' => 'private_user', 'name' => 'Test User 3', 'private' => true, 'whitelist-only' => false],
|
||||
['id' => self::USER_WLON_ID, 'login' => 'whitelist_only_user', 'name' => 'Test User 4', 'private' => false, 'whitelist-only' => true],
|
||||
['id' => self::USER_PRWL_ID, 'login' => 'private_whitelist_only_user', 'name' => 'Test User 4', 'private' => false, 'whitelist-only' => true],
|
||||
['id' => self::USER_UNNM_ID, 'login' => 'unnamed_user', 'name' => null, 'private' => false, 'whitelist-only' => false],
|
||||
];
|
||||
|
||||
public function load(ObjectManager $om)
|
||||
{
|
||||
$userId = 99999;
|
||||
|
||||
foreach ($this->users as $userData) {
|
||||
$user = new User($userId--, new \DateTime(), $userData['login'], $userData['name']);
|
||||
$user = new User($userData['id'], new \DateTime(), $userData['login'], $userData['name']);
|
||||
$user->updatePrivacy(!$userData['private'], $userData['whitelist-only']);
|
||||
|
||||
$om->persist($user);
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
|||
/**
|
||||
* @ORM\Table(name="comments", schema="posts", indexes={
|
||||
* @ORM\Index(name="idx_comment_created_at", columns={"created_at"})
|
||||
* }, uniqueConstraints={
|
||||
* @ORM\UniqueConstraint(name="unique_post_id_comment_number", columns={"post_id", "number"})
|
||||
* })
|
||||
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Blogs\CommentRepository")
|
||||
*/
|
||||
|
@ -64,17 +62,10 @@ class Comment
|
|||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="number", type="integer", unique=true)
|
||||
* @ORM\Column(name="number", type="smallint")
|
||||
*/
|
||||
private $number;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*
|
||||
* @ORM\Column(name="to_number", type="integer", nullable=true)
|
||||
*/
|
||||
private $toNumber;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*
|
||||
|
@ -94,36 +85,26 @@ class Comment
|
|||
*/
|
||||
private $files;
|
||||
|
||||
/**
|
||||
* @var Comment|null
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment", inversedBy="children")
|
||||
* @ORM\JoinColumn(name="parent_id", nullable=true)
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
public function __construct(
|
||||
string $text,
|
||||
\DateTime $createdAt,
|
||||
bool $rec,
|
||||
Post $post,
|
||||
int $number,
|
||||
?int $toNumber,
|
||||
User $author,
|
||||
array $files
|
||||
) {
|
||||
$this->text = $text;
|
||||
$this->createdAt = $createdAt;
|
||||
$this->rec = $rec;
|
||||
$this->post = $post;
|
||||
$this->number = $number;
|
||||
$this->toNumber = $toNumber;
|
||||
$this->author = $author;
|
||||
/**
|
||||
* @var Comment[]|ArrayCollection
|
||||
*
|
||||
* @ORM\OneToMany(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment", fetch="EXTRA_LAZY", mappedBy="parent")
|
||||
*/
|
||||
private $children;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->files = new ArrayCollection();
|
||||
foreach ($files as $file) {
|
||||
if (!($file instanceof File)) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'$files array must contain only \'%s\' objects. %s given.',
|
||||
\is_object($file) ? \get_class($file) : \gettype($file)
|
||||
));
|
||||
}
|
||||
|
||||
$this->files->add($file);
|
||||
}
|
||||
$this->children = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
|
@ -131,41 +112,95 @@ class Comment
|
|||
return $this->id;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTime $createdAt): self
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): \DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setText(string $text): self
|
||||
{
|
||||
$this->text = $text;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getText(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function setRec(bool $rec): self
|
||||
{
|
||||
$this->rec = $rec;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRec(): bool
|
||||
{
|
||||
return $this->rec;
|
||||
}
|
||||
|
||||
public function getRec(): bool
|
||||
{
|
||||
return $this->rec;
|
||||
}
|
||||
|
||||
public function getPost(): Post
|
||||
{
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
public function setPost(Post $post): self
|
||||
{
|
||||
$this->post = $post;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setNumber(int $number): self
|
||||
{
|
||||
$this->number = $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumber(): int
|
||||
{
|
||||
return $this->number;
|
||||
}
|
||||
|
||||
public function getToNumber(): ?int
|
||||
{
|
||||
return $this->toNumber;
|
||||
}
|
||||
|
||||
public function getAuthor(): User
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function setAuthor(User $author): self
|
||||
{
|
||||
$this->author = $author;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addFile(File $files): self
|
||||
{
|
||||
$this->files[] = $files;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeFile(File $files): void
|
||||
{
|
||||
$this->files->removeElement($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return File[]|ArrayCollection
|
||||
*/
|
||||
|
@ -174,22 +209,52 @@ class Comment
|
|||
return $this->files;
|
||||
}
|
||||
|
||||
public function delete(): self
|
||||
public function getParent(): ?Comment
|
||||
{
|
||||
$this->deleted = true;
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function setParent(Comment $parent): self
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function restore(): self
|
||||
public function setDeleted(bool $deleted): self
|
||||
{
|
||||
$this->deleted = false;
|
||||
$this->deleted = $deleted;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDeleted(): bool
|
||||
{
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
public function isDeleted(): bool
|
||||
{
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
public function addChild(Comment $children): self
|
||||
{
|
||||
$this->children[] = $children;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeChild(Comment $children): void
|
||||
{
|
||||
$this->children->removeElement($children);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Comment[]|ArrayCollection
|
||||
*/
|
||||
public function getChildren(): iterable
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,13 +69,6 @@ class Post
|
|||
*/
|
||||
private $deleted = false;
|
||||
|
||||
/**
|
||||
* @var bool Status of point-tools subscription to the post (to receive WS updates)
|
||||
*
|
||||
* @ORM\Column(name="is_subscribed", type="boolean", options={"default": false})
|
||||
*/
|
||||
private $subscribed = false;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*
|
||||
|
@ -110,7 +103,7 @@ class Post
|
|||
private $comments;
|
||||
|
||||
|
||||
public function __construct(string $id, User $author, \DateTime $createdAt, string $type = self::TYPE_POST)
|
||||
public function __construct(string $id, User $author, \DateTime $createdAt, string $type)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->author = $author;
|
||||
|
@ -223,25 +216,6 @@ class Post
|
|||
{
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
public function isSubscribed(): bool
|
||||
{
|
||||
return $this->subscribed;
|
||||
}
|
||||
|
||||
public function subscribe(): self
|
||||
{
|
||||
$this->subscribed = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function unsubscribe(): self
|
||||
{
|
||||
$this->subscribed = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPrivate(bool $private): self
|
||||
{
|
||||
|
@ -263,6 +237,7 @@ class Post
|
|||
public function addComment(Comment $comment): self
|
||||
{
|
||||
$this->comments[] = $comment;
|
||||
$comment->setPost($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
|||
|
||||
/**
|
||||
* @ORM\Table(name="telegram_accounts", schema="users", indexes={
|
||||
* @ORM\Index(name="subscriber_notification_idx", columns={"subscriber_notification"}, options={"where": "(subscriber_notification = true)"}),
|
||||
* @ORM\Index(name="rename_notification_idx", columns={"rename_notification"}, options={"where": "(rename_notification = true)"}),
|
||||
* @ORM\Index(name="subscriber_notification_idx", columns={"subscriber_notification"}, options={"where": "subscriber_notification = TRUE"}),
|
||||
* @ORM\Index(name="rename_notification_idx", columns={"rename_notification"}, options={"where": "rename_notification = TRUE"}),
|
||||
* })
|
||||
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository")
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
|
|
|
@ -7,7 +7,6 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
|
||||
/**
|
||||
* @ORM\Table(name="users", schema="users", indexes={
|
||||
* @ORM\Index(name="idx_user_login", columns={"login"}),
|
||||
* @ORM\Index(name="idx_user_public", columns={"public"}),
|
||||
* @ORM\Index(name="idx_user_removed", columns={"is_removed"})
|
||||
* })
|
||||
|
@ -191,24 +190,15 @@ class User
|
|||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function updateCreatedAt(\DateTime $date): self
|
||||
{
|
||||
$this->createdAt = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?\DateTime
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function updatePrivacy(bool $public, bool $whitelistOnly): self
|
||||
public function updatePrivacy(?bool $public, ?bool $whitelistOnly): void
|
||||
{
|
||||
$this->public = $public;
|
||||
$this->whitelistOnly = $whitelistOnly;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isPublic(): ?bool
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
|
||||
|
||||
class PostNotFoundException extends NotFoundException
|
||||
{
|
||||
/** @var string */
|
||||
private $id;
|
||||
|
||||
public function __construct(string $id, \Exception $previous)
|
||||
{
|
||||
parent::__construct('Post not found', 0, $previous);
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
|
@ -4,11 +4,16 @@ namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
|
|||
|
||||
class UserNotFoundException extends NotFoundException
|
||||
{
|
||||
/** @var int */
|
||||
private $userId;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $userId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $login;
|
||||
|
||||
/** @var string */
|
||||
private $login;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Exception\WebSocket;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class UnsupportedTypeException extends RuntimeException
|
||||
{
|
||||
}
|
|
@ -2,6 +2,10 @@ 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
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost:
|
||||
exclusion_policy: none
|
||||
access_type: public_method
|
||||
properties:
|
||||
post:
|
||||
serialized_name: 'post'
|
||||
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post'
|
||||
max_depth: 2
|
||||
comments:
|
||||
serialized_name: 'comments'
|
||||
type: 'array<Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment>'
|
||||
max_depth: 2
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message:
|
||||
exclusion_policy: none
|
||||
properties:
|
||||
a:
|
||||
type: string
|
||||
serialized_name: 'a'
|
||||
author:
|
||||
type: string
|
||||
serialized_name: 'author'
|
||||
authorId:
|
||||
type: int
|
||||
serialized_name: 'author_id'
|
||||
authorName:
|
||||
type: string
|
||||
serialized_name: 'author_name'
|
||||
commentId:
|
||||
type: int
|
||||
serialized_name: 'comment_id'
|
||||
cut:
|
||||
type: bool
|
||||
serialized_name: 'cut'
|
||||
files:
|
||||
type: array<string>
|
||||
serialized_name: 'files'
|
||||
html:
|
||||
type: string
|
||||
serialized_name: 'html'
|
||||
link:
|
||||
type: string
|
||||
serialized_name: 'link'
|
||||
postId:
|
||||
type: string
|
||||
serialized_name: 'post_id'
|
||||
postAuthorId:
|
||||
type: int
|
||||
serialized_name: 'post_author_id'
|
||||
private:
|
||||
type: bool
|
||||
serialized_name: 'private'
|
||||
rcid:
|
||||
type: int
|
||||
serialized_name: 'rcid'
|
||||
tags:
|
||||
type: array<string>
|
||||
serialized_name: 'tags'
|
||||
text:
|
||||
type: string
|
||||
serialized_name: 'text'
|
||||
title:
|
||||
type: string
|
||||
serialized_name: 'title'
|
||||
toCommentId:
|
||||
type: string
|
||||
serialized_name: 'to_comment_id'
|
||||
toText:
|
||||
type: string
|
||||
serialized_name: 'to_text'
|
||||
toUsers:
|
||||
type: array<string>
|
||||
serialized_name: 'to_users'
|
|
@ -5,9 +5,6 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
|
|||
use GuzzleHttp\ClientInterface;
|
||||
use JMS\Serializer\SerializerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
|
||||
use Skobkin\Bundle\PointToolsBundle\Exception\Api\{NotFoundException, PostNotFoundException};
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory;
|
||||
|
||||
/**
|
||||
|
@ -15,8 +12,6 @@ use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory;
|
|||
*/
|
||||
class PostApi extends AbstractApi
|
||||
{
|
||||
private const PREFIX = '/api/post/';
|
||||
|
||||
/**
|
||||
* @var PostFactory
|
||||
*/
|
||||
|
@ -29,24 +24,4 @@ class PostApi extends AbstractApi
|
|||
|
||||
$this->postFactory = $postFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PostNotFoundException
|
||||
*/
|
||||
public function getById(string $id): Post
|
||||
{
|
||||
try {
|
||||
$postData = $this->getGetJsonData(
|
||||
self::PREFIX.$id,
|
||||
[],
|
||||
MetaPost::class
|
||||
);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new PostNotFoundException($id, $e);
|
||||
}
|
||||
|
||||
// Not catching ForbiddenException right now
|
||||
|
||||
return $this->postFactory->findOrCreateFromDtoWithContent($postData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use JMS\Serializer\{DeserializationContext, SerializerInterface};
|
||||
use JMS\Serializer\{
|
||||
DeserializationContext, SerializerInterface
|
||||
};
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{Auth, User as UserDTO};
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||
|
|
|
@ -6,8 +6,6 @@ use Psr\Log\LoggerInterface;
|
|||
|
||||
abstract class AbstractFactory
|
||||
{
|
||||
public const DATE_FORMAT = 'Y-m-d_H:i:s';
|
||||
|
||||
/** @var LoggerInterface */
|
||||
protected $logger;
|
||||
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message;
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment;
|
||||
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\{CommentRepository, PostRepository};
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\Api\PostApi;
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\{AbstractFactory, UserFactory};
|
||||
|
||||
class CommentFactory extends AbstractFactory
|
||||
|
@ -20,45 +17,12 @@ class CommentFactory extends AbstractFactory
|
|||
/** @var UserFactory */
|
||||
private $userFactory;
|
||||
|
||||
/** @var PostApi */
|
||||
private $postApi;
|
||||
|
||||
|
||||
public function __construct(LoggerInterface $logger, CommentRepository $commentRepository, PostRepository $postRepository, UserFactory $userFactory, PostApi $postApi)
|
||||
public function __construct(LoggerInterface $logger, CommentRepository $commentRepository, PostRepository $postRepository, UserFactory $userFactory)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->userFactory = $userFactory;
|
||||
$this->commentRepository = $commentRepository;
|
||||
$this->postRepository = $postRepository;
|
||||
$this->postApi = $postApi;
|
||||
}
|
||||
|
||||
public function findOrCreateFromWebsocketMessage(Message $message): Comment
|
||||
{
|
||||
if ($message->isValid()) {
|
||||
throw new \InvalidArgumentException('Comment is invalid');
|
||||
}
|
||||
if ($message->isComment()) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Invalid Message object provided. %s expected, %s given',
|
||||
Message::TYPE_COMMENT,
|
||||
$message->getA()
|
||||
));
|
||||
}
|
||||
|
||||
if (null === $comment = $this->commentRepository->findOneBy(['post' => $post, 'number' => $message->getCommentId()])) {
|
||||
$author = $this->userFactory->findOrCreateFromIdLoginAndName(
|
||||
$message->getAuthorId(),
|
||||
$message->getAuthor(),
|
||||
$message->getAuthorName()
|
||||
);
|
||||
|
||||
if (null === $post = $this->postRepository->find($message->getPostId())) {
|
||||
$post = $this->postApi->getById($message->getPostId());
|
||||
}
|
||||
|
||||
// TODO
|
||||
//$comment = new Comment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,11 +4,10 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
|
|||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{MetaPost, Post as ApiPost, PostsPage};
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message as WebsocketMessage;
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{MetaPost, Post as PostDTO, PostsPage};
|
||||
use Skobkin\Bundle\PointToolsBundle\Entity\{Blogs\Post, Blogs\PostTag, User};
|
||||
use Skobkin\Bundle\PointToolsBundle\Exception\{Api\InvalidResponseException, Factory\Blog\InvalidDataException};
|
||||
use Skobkin\Bundle\PointToolsBundle\Repository\{Blogs\PostRepository, UserRepository};
|
||||
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository;
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\{AbstractFactory, UserFactory};
|
||||
|
||||
class PostFactory extends AbstractFactory
|
||||
|
@ -19,9 +18,6 @@ class PostFactory extends AbstractFactory
|
|||
/** @var PostRepository */
|
||||
private $postRepository;
|
||||
|
||||
/** @var UserRepository */
|
||||
private $userRepository;
|
||||
|
||||
/** @var UserFactory */
|
||||
private $userFactory;
|
||||
|
||||
|
@ -39,7 +35,6 @@ class PostFactory extends AbstractFactory
|
|||
LoggerInterface $logger,
|
||||
EntityManagerInterface $em,
|
||||
PostRepository $postRepository,
|
||||
UserRepository $userRepository,
|
||||
UserFactory $userFactory,
|
||||
FileFactory $fileFactory,
|
||||
CommentFactory $commentFactory,
|
||||
|
@ -48,7 +43,6 @@ class PostFactory extends AbstractFactory
|
|||
parent::__construct($logger);
|
||||
$this->em = $em;
|
||||
$this->postRepository = $postRepository;
|
||||
$this->userRepository = $userRepository;
|
||||
$this->userFactory = $userFactory;
|
||||
$this->fileFactory = $fileFactory;
|
||||
$this->commentFactory = $commentFactory;
|
||||
|
@ -98,6 +92,8 @@ class PostFactory extends AbstractFactory
|
|||
/**
|
||||
* Create full post with tags, files and comments
|
||||
*
|
||||
* @todo Implement comments
|
||||
*
|
||||
* @throws InvalidDataException
|
||||
*/
|
||||
public function findOrCreateFromDtoWithContent(MetaPost $metaPost): Post
|
||||
|
@ -115,7 +111,7 @@ class PostFactory extends AbstractFactory
|
|||
throw $e;
|
||||
}
|
||||
|
||||
$post = $this->findOrCreateFromApiDto($postData, $author);
|
||||
$post = $this->findOrCreateFromDto($postData, $author);
|
||||
|
||||
try {
|
||||
$this->updatePostTags($post, $postData->getTags() ?: []);
|
||||
|
@ -131,62 +127,10 @@ class PostFactory extends AbstractFactory
|
|||
throw $e;
|
||||
}
|
||||
|
||||
// @TODO implement comments
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
public function findOrCreateFromWebsocketDto(WebsocketMessage $message): Post
|
||||
{
|
||||
if (!$message->isValid()) {
|
||||
throw new InvalidDataException('Invalid post data');
|
||||
}
|
||||
if (!$message->isPost()) {
|
||||
throw new \LogicException(sprintf(
|
||||
'Incorrect message type received. \'post\' expected \'%s\' given',
|
||||
$message->getA()
|
||||
));
|
||||
}
|
||||
|
||||
if (null === $post = $this->postRepository->find($message->getPostId())) {
|
||||
$author = $this->userFactory->findOrCreateFromIdLoginAndName(
|
||||
$message->getAuthorId(),
|
||||
$message->getAuthor(),
|
||||
$message->getAuthorName()
|
||||
);
|
||||
|
||||
$post = new Post(
|
||||
$message->getPostId(),
|
||||
$author,
|
||||
new \DateTime(),
|
||||
Post::TYPE_POST
|
||||
);
|
||||
$this->postRepository->add($post);
|
||||
}
|
||||
|
||||
$post
|
||||
->setText($message->getText())
|
||||
->setPrivate((bool) $message->getPrivate())
|
||||
;
|
||||
|
||||
try {
|
||||
$this->updatePostTags($post, $message->getTags() ?: []);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Error while updating post tags');
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->updatePostFiles($post, $message->getFiles() ?: []);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Error while updating post files');
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
private function findOrCreateFromApiDto(ApiPost $postData, User $author): Post
|
||||
private function findOrCreateFromDto(PostDTO $postData, User $author): Post
|
||||
{
|
||||
if (null === ($post = $this->postRepository->find($postData->getId()))) {
|
||||
// Creating new post
|
||||
|
@ -201,7 +145,7 @@ class PostFactory extends AbstractFactory
|
|||
|
||||
$post
|
||||
->setText($postData->getText())
|
||||
->setPrivate((bool) $postData->getPrivate())
|
||||
->setPrivate($postData->getPrivate())
|
||||
;
|
||||
|
||||
return $post;
|
||||
|
|
|
@ -10,9 +10,12 @@ use Skobkin\Bundle\PointToolsBundle\Exception\Factory\InvalidUserDataException;
|
|||
|
||||
class UserFactory extends AbstractFactory
|
||||
{
|
||||
public const DATE_FORMAT = 'Y-m-d_H:i:s';
|
||||
|
||||
/** @var UserRepository */
|
||||
private $userRepository;
|
||||
|
||||
|
||||
public function __construct(LoggerInterface $logger, UserRepository $userRepository)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
@ -20,25 +23,27 @@ class UserFactory extends AbstractFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* @param UserDTO $userData
|
||||
*
|
||||
* @return User
|
||||
*
|
||||
* @throws InvalidUserDataException
|
||||
*/
|
||||
public function findOrCreateFromDTO(UserDTO $userData): User
|
||||
{
|
||||
// @todo LOG
|
||||
|
||||
if (!$userData->isValid()) {
|
||||
throw new InvalidUserDataException('Invalid user data', $userData);
|
||||
}
|
||||
|
||||
$createdAt = \DateTime::createFromFormat(self::DATE_FORMAT, $userData->getCreated()) ?: new \DateTime();
|
||||
|
||||
/** @var User $user */
|
||||
if (null === ($user = $this->userRepository->find($userData->getId()))) {
|
||||
$user = new User(
|
||||
$userData->getId(),
|
||||
$createdAt
|
||||
\DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime()
|
||||
);
|
||||
$this->userRepository->add($user);
|
||||
} else {
|
||||
$user->updateCreatedAt($createdAt);
|
||||
}
|
||||
|
||||
$user->updateLoginAndName($userData->getLogin(), $userData->getName());
|
||||
|
@ -50,21 +55,6 @@ class UserFactory extends AbstractFactory
|
|||
return $user;
|
||||
}
|
||||
|
||||
public function findOrCreateFromIdLoginAndName(int $id, string $login, ?string $name): User
|
||||
{
|
||||
/** @var User $user */
|
||||
if (null === $user = $this->userRepository->find($id)) {
|
||||
// We're using current date now but next time when we'll be updating user from API it'll be fixed
|
||||
$user = new User($id, new \DateTime(), $login, $name);
|
||||
$this->userRepository->add($user);
|
||||
} else {
|
||||
// @todo update login?
|
||||
// Probably don't because no name in the WS message (or maybe after PR to point?)
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User[]
|
||||
*/
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Skobkin\Bundle\PointToolsBundle\Service\WebSocket;
|
||||
|
||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message;
|
||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\{CommentFactory, PostFactory};
|
||||
|
||||
class WebSocketMessageProcessor
|
||||
{
|
||||
/** @var PostFactory */
|
||||
private $postFactory;
|
||||
|
||||
/** @var CommentFactory */
|
||||
private $commentFactory;
|
||||
|
||||
public function __construct(PostFactory $postFactory, CommentFactory $commentFactory)
|
||||
{
|
||||
$this->postFactory = $postFactory;
|
||||
$this->commentFactory = $commentFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true on success (all data saved)
|
||||
*/
|
||||
public function processMessage(Message $message): bool
|
||||
{
|
||||
if (!$message->isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $message->isPost():
|
||||
return $this->processPost($message);
|
||||
break;
|
||||
|
||||
case $message->isComment():
|
||||
return $this->processComment($message);
|
||||
break;
|
||||
|
||||
case $message->isCommentRecommendation():
|
||||
return $this->processRecommendation($message);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processPost(Message $postData): bool
|
||||
{
|
||||
$this->postFactory->findOrCreateFromWebsocketDto($postData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processComment(Message $commentData): bool
|
||||
{
|
||||
// Not done yet
|
||||
return false;
|
||||
|
||||
$this->commentFactory->findOrCreateFromWebsocketMessage($commentData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processRecommendation(Message $recommendData): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
namespace Tests\Skobkin\PointToolsBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Client;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
class MainControllerTest extends WebTestCase
|
||||
{
|
||||
public function testUserSearch()
|
||||
public function testUserSearch(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
$crawler = $client->request('GET', '/');
|
||||
|
@ -19,7 +20,7 @@ class MainControllerTest extends WebTestCase
|
|||
$this->assertTrue($client->getResponse()->isRedirect('/user/testuser'), 'Redirect to testuser\'s page didn\'t happen');
|
||||
}
|
||||
|
||||
public function testNonExistingUserSearch()
|
||||
public function testNonExistingUserSearch(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
$crawler = $client->request('GET', '/');
|
||||
|
@ -49,7 +50,7 @@ class MainControllerTest extends WebTestCase
|
|||
$this->assertEquals(' Login not found', $firstError->text(), 'Incorrect error text');
|
||||
}
|
||||
|
||||
public function testUserStats()
|
||||
public function testUserStats(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
$crawler = $client->request('GET', '/');
|
||||
|
@ -75,14 +76,10 @@ class MainControllerTest extends WebTestCase
|
|||
|
||||
/**
|
||||
* Tests AJAX user search autocomplete and returns JSON response string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function testAjaxUserAutoComplete()
|
||||
public function testAjaxUserAutoComplete(): string
|
||||
{
|
||||
$client = static::createClient();
|
||||
// We need to search all test user with 'testuser5' included which will test the code against null-string problem in User#getName()
|
||||
$client->request('GET', '/ajax/users/search/testuser');
|
||||
$client = $this->createClientForAjaxUserSearchByLogin('testuser');
|
||||
|
||||
$this->assertTrue($client->getResponse()->headers->contains('Content-Type', 'application/json'), 'Response has "Content-Type" = "application/json"');
|
||||
|
||||
|
@ -91,26 +88,22 @@ class MainControllerTest extends WebTestCase
|
|||
|
||||
/**
|
||||
* @depends testAjaxUserAutoComplete
|
||||
*
|
||||
* @param $json
|
||||
*/
|
||||
public function testAjaxUserAutoCompleteHasOptions($json)
|
||||
public function testAjaxUserAutoCompleteHasOptions(string $json): array
|
||||
{
|
||||
$data = json_decode($json);
|
||||
$data = json_decode($json, true);
|
||||
|
||||
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
||||
$this->assertTrue(is_array($data), 'JSON data is array');
|
||||
$this->assertCount(5, $data, 'Array has 5 elements');
|
||||
$this->assertCount(2, $data, 'Array has 2 elements');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAjaxUserAutoCompleteHasOptions
|
||||
*
|
||||
* @param array $users
|
||||
*/
|
||||
public function testAjaxUserAutoCompleteHasValidUserObjects(array $users)
|
||||
public function testAjaxUserAutoCompleteHasValidUserObjects(array $users): void
|
||||
{
|
||||
foreach ($users as $key => $user) {
|
||||
$this->assertTrue(array_key_exists('login', $user), sprintf('%d row of result has \'login\' field', $key));
|
||||
|
@ -118,7 +111,43 @@ class MainControllerTest extends WebTestCase
|
|||
}
|
||||
}
|
||||
|
||||
public function testAjaxUserAutoCompleteForNonExistingUser()
|
||||
/**
|
||||
* Tests AJAX user search autocomplete for unnamed user and returns JSON response string
|
||||
*/
|
||||
public function testAjaxUserAutoCompleteForUnnamedUser(): string
|
||||
{
|
||||
$client = $this->createClientForAjaxUserSearchByLogin('unnamed_user');
|
||||
|
||||
$this->assertTrue($client->getResponse()->headers->contains('Content-Type', 'application/json'), 'Response has "Content-Type" = "application/json"');
|
||||
|
||||
return $client->getResponse()->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAjaxUserAutoCompleteForUnnamedUser
|
||||
*/
|
||||
public function testAjaxUserAutoCompleteHasOptionsForUnnamedUser(string $json): array
|
||||
{
|
||||
$data = json_decode($json, true);
|
||||
|
||||
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
||||
$this->assertInternalType('array', $data, 'JSON data is array');
|
||||
$this->assertCount(1, $data, 'Array has 1 elements');
|
||||
|
||||
return reset($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAjaxUserAutoCompleteHasOptionsForUnnamedUser
|
||||
*/
|
||||
public function testAjaxUserAutoCompleteHasValidUserObjectsForUnnamedUser(array $user): void
|
||||
{
|
||||
$this->assertTrue(array_key_exists('login', $user), 'Result has \'login\' field');
|
||||
$this->assertTrue(array_key_exists('name', $user), 'Result has \'name\' field');
|
||||
$this->assertEquals(true, ('' === $user['name'] || null === $user['name']), 'User name is empty string or null');
|
||||
}
|
||||
|
||||
public function testAjaxUserAutoCompleteIsEmptyForNonExistingUser(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
$client->request('GET', '/ajax/users/search/aksdjhaskdjhqwhdgqkjwhdgkjah');
|
||||
|
@ -128,7 +157,15 @@ class MainControllerTest extends WebTestCase
|
|||
$data = json_decode($client->getResponse()->getContent());
|
||||
|
||||
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
||||
$this->assertTrue(is_array($data), 'JSON data is array');
|
||||
$this->assertEquals(0, count($data), 'Array has no elements');
|
||||
$this->assertInternalType('array', $data, 'JSON data is array');
|
||||
$this->assertCount(0, $data, 'Array has no elements');
|
||||
}
|
||||
|
||||
private function createClientForAjaxUserSearchByLogin(string $login): Client
|
||||
{
|
||||
$client = static::createClient();
|
||||
$client->request('GET', '/ajax/users/search/'.$login);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
namespace Tests\Skobkin\PointToolsBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Skobkin\Bundle\PointToolsBundle\DataFixtures\ORM\LoadPostData;
|
||||
use Symfony\Bundle\FrameworkBundle\{Client, Test\WebTestCase};
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
class PostControllerTest extends WebTestCase
|
||||
{
|
||||
public function testNonExistingPostPage()
|
||||
{
|
||||
$client = static::createClient();
|
||||
$client->request('GET', '/nonexistingpost');
|
||||
$client = $this->createClientForPostId('nonexistingpost');
|
||||
|
||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for non-existing post');
|
||||
}
|
||||
|
@ -20,12 +20,11 @@ class PostControllerTest extends WebTestCase
|
|||
*/
|
||||
public function testShortPostPageIsOk()
|
||||
{
|
||||
$client = static::createClient();
|
||||
$crawler = $client->request('GET', '/shortpost');
|
||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_SHORT);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isOk(), '200 response code for existing post');
|
||||
|
||||
return $crawler;
|
||||
return $client->getCrawler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,4 +57,33 @@ class PostControllerTest extends WebTestCase
|
|||
$this->assertEquals(1, $p->count(), '.post-text has zero or more than one paragraphs');
|
||||
$this->assertEquals('Test short post', $p->text(), '.post-text has no correct post text');
|
||||
}
|
||||
|
||||
public function testPrivateUserPostForbidden()
|
||||
{
|
||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_PR_USER);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for private user\'s post');
|
||||
}
|
||||
|
||||
public function testWhitelistOnlyUserPostForbidden()
|
||||
{
|
||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_WL_USER);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for whitelist-only user\'s post');
|
||||
}
|
||||
|
||||
public function testPrivateWhitelistOnlyUserPostForbidden()
|
||||
{
|
||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_PR_WL_USER);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for private whitelist-only user\'s post');
|
||||
}
|
||||
|
||||
private function createClientForPostId(string $id): Client
|
||||
{
|
||||
$client = static::createClient();
|
||||
$client->request('GET', '/'.$id);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class UserRepositoryTest extends KernelTestCase
|
|||
{
|
||||
$users = $this->userRepo->findAll();
|
||||
|
||||
$this->assertCount(5, $users, 'Not exactly 5 users in the databas');
|
||||
$this->assertCount(6, $users, 'Not exactly 6 users in the databas');
|
||||
}
|
||||
|
||||
public function testFindOneByLogin()
|
||||
|
@ -58,14 +58,14 @@ class UserRepositoryTest extends KernelTestCase
|
|||
// Searching LIKE %stus% (testuserX)
|
||||
$users = $this->userRepo->findUsersLikeLogin('stus');
|
||||
|
||||
$this->assertCount(5, $users, 'Repository found not exactly 5 users');
|
||||
$this->assertCount(2, $users, 'Repository found not exactly 5 users');
|
||||
}
|
||||
|
||||
public function testGetUsersCount()
|
||||
{
|
||||
$count = $this->userRepo->getUsersCount();
|
||||
|
||||
$this->assertEquals(5, $count, 'Counted not exactly 5 users');
|
||||
$this->assertEquals(6, $count, 'Counted not exactly 5 users');
|
||||
}
|
||||
|
||||
public function testFindUserSubscribersById()
|
||||
|
|
Loading…
Reference in a new issue