Add new Helper to process Ignore Origin rules and RulerZ operator

This commits adds a new helper like RuleBasedTagger for processing
ignore origin rules. It also adds a new custom RulerZ operator for the
'~' pattern matching rule.

Renames 'pattern' with '_all' in IgnoreOriginRule entity.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
This commit is contained in:
Kevin Decherf 2019-08-11 23:51:55 +02:00
parent 24230a5130
commit f39c5a2a70
6 changed files with 300 additions and 4 deletions

View file

@ -11,7 +11,6 @@ use Symfony\Component\Validator\Constraints as Assert;
*
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\IgnoreOriginInstanceRuleRepository")
* @ORM\Table(name="`ignore_origin_instance_rule`")
* @ORM\Entity
*/
class IgnoreOriginInstanceRule implements IgnoreOriginRuleInterface, RuleInterface
{
@ -30,7 +29,7 @@ class IgnoreOriginInstanceRule implements IgnoreOriginRuleInterface, RuleInterfa
* @Assert\NotBlank()
* @Assert\Length(max=255)
* @RulerZAssert\ValidRule(
* allowed_variables={"host","pattern"},
* allowed_variables={"host","_all"},
* allowed_operators={"=","~"}
* )
* @ORM\Column(name="rule", type="string", nullable=false)

View file

@ -11,7 +11,6 @@ use Symfony\Component\Validator\Constraints as Assert;
*
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\IgnoreOriginUserRuleRepository")
* @ORM\Table(name="`ignore_origin_user_rule`")
* @ORM\Entity
*/
class IgnoreOriginUserRule implements IgnoreOriginRuleInterface, RuleInterface
{
@ -30,7 +29,7 @@ class IgnoreOriginUserRule implements IgnoreOriginRuleInterface, RuleInterface
* @Assert\NotBlank()
* @Assert\Length(max=255)
* @RulerZAssert\ValidRule(
* allowed_variables={"host","pattern"},
* allowed_variables={"host","_all"},
* allowed_operators={"=","~"}
* )
* @ORM\Column(name="rule", type="string", nullable=false)

View file

@ -0,0 +1,50 @@
<?php
namespace Wallabag\CoreBundle\Helper;
use Psr\Log\LoggerInterface;
use RulerZ\RulerZ;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Repository\IgnoreOriginInstanceRuleRepository;
class RuleBasedIgnoreOriginProcessor
{
protected $rulerz;
protected $logger;
protected $ignoreOriginInstanceRuleRepository;
public function __construct(RulerZ $rulerz, LoggerInterface $logger, IgnoreOriginInstanceRuleRepository $ignoreOriginInstanceRuleRepository)
{
$this->rulerz = $rulerz;
$this->logger = $logger;
$this->ignoreOriginInstanceRuleRepository = $ignoreOriginInstanceRuleRepository;
}
/**
* @param Entry $entry Entry to process
*
* @return bool
*/
public function process(Entry $entry)
{
$url = $entry->getUrl();
$userRules = $entry->getUser()->getConfig()->getIgnoreOriginRules()->toArray();
$rules = array_merge($this->ignoreOriginInstanceRuleRepository->findAll(), $userRules);
$parsed_url = parse_url($url);
// We add the former url as a new key _all for pattern matching
$parsed_url['_all'] = $url;
foreach ($rules as $rule) {
if ($this->rulerz->satisfies($parsed_url, $rule->getRule())) {
$this->logger->info('Origin url matching ignore rule.', [
'rule' => $rule->getRule(),
]);
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Wallabag\CoreBundle\Operator\PHP;
/**
* Provides a "~" operator used for ignore origin rules.
*
* It asserts that a subject matches a given regexp pattern, in a
* case-insensitive way.
*
* This operator will be used to compile ignore origin rules in PHP, usable
* directly on Entry objects for instance.
* It's registered in RulerZ using a service (wallabag.operator.array.pattern_matches);
*/
class PatternMatches
{
public function __invoke($subject, $pattern)
{
$count = preg_match("`$pattern`i", $subject);
return \is_int($count) && $count > 0;
}
}

View file

@ -92,6 +92,7 @@ services:
arguments:
- "@wallabag_core.graby"
- "@wallabag_core.rule_based_tagger"
- "@wallabag_core.rule_based_ignore_origin_processor"
- "@validator"
- "@logger"
- '%wallabag_core.fetching_error_message%'
@ -110,6 +111,13 @@ services:
- "@wallabag_core.entry_repository"
- "@logger"
wallabag_core.rule_based_ignore_origin_processor:
class: Wallabag\CoreBundle\Helper\RuleBasedIgnoreOriginProcessor
arguments:
- "@rulerz"
- "@logger"
- "@wallabag_core.ignore_origin_instance_rule_repository"
# repository as a service
wallabag_core.entry_repository:
class: Wallabag\CoreBundle\Repository\EntryRepository
@ -164,6 +172,11 @@ services:
tags:
- { name: rulerz.operator, target: doctrine, operator: notmatches, inline: true }
wallabag.operator.array.pattern_matches:
class: Wallabag\CoreBundle\Operator\PHP\PatternMatches
tags:
- { name: rulerz.operator, target: native, operator: "~" }
wallabag_core.helper.redirect:
class: Wallabag\CoreBundle\Helper\Redirect
arguments:

View file

@ -0,0 +1,212 @@
<?php
namespace Tests\Wallabag\CoreBundle\Helper;
use Monolog\Handler\TestHandler;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Wallabag\CoreBundle\Entity\Config;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\IgnoreOriginInstanceRule;
use Wallabag\CoreBundle\Entity\IgnoreOriginUserRule;
use Wallabag\CoreBundle\Helper\RuleBasedIgnoreOriginProcessor;
use Wallabag\UserBundle\Entity\User;
class RuleBasedIgnoreOriginProcessorTest extends TestCase
{
private $rulerz;
private $processor;
private $ignoreOriginInstanceRuleRepository;
private $logger;
private $handler;
public function setUp()
{
$this->rulerz = $this->getRulerZMock();
$this->logger = $this->getLogger();
$this->ignoreOriginInstanceRuleRepository = $this->getIgnoreOriginInstanceRuleRepositoryMock();
$this->handler = new TestHandler();
$this->logger->pushHandler($this->handler);
$this->processor = new RuleBasedIgnoreOriginProcessor($this->rulerz, $this->logger, $this->ignoreOriginInstanceRuleRepository);
}
public function testProcessWithNoRule()
{
$user = $this->getUser();
$entry = new Entry($user);
$entry->setUrl('http://example.com/hello-world');
$this->ignoreOriginInstanceRuleRepository
->expects($this->once())
->method('findAll')
->willReturn([]);
$this->rulerz
->expects($this->never())
->method('satisfies');
$result = $this->processor->process($entry);
$this->assertFalse($result);
}
public function testProcessWithNoMatchingRule()
{
$userRule = $this->getIgnoreOriginUserRule('rule as string');
$user = $this->getUser([$userRule]);
$entry = new Entry($user);
$entry->setUrl('http://example.com/hello-world');
$this->ignoreOriginInstanceRuleRepository
->expects($this->once())
->method('findAll')
->willReturn([]);
$this->rulerz
->expects($this->once())
->method('satisfies')
->willReturn(false);
$result = $this->processor->process($entry);
$this->assertFalse($result);
}
public function testProcessWithAMatchingRule()
{
$userRule = $this->getIgnoreOriginUserRule('rule as string');
$user = $this->getUser([$userRule]);
$entry = new Entry($user);
$entry->setUrl('http://example.com/hello-world');
$this->ignoreOriginInstanceRuleRepository
->expects($this->once())
->method('findAll')
->willReturn([]);
$this->rulerz
->expects($this->once())
->method('satisfies')
->willReturn(true);
$result = $this->processor->process($entry);
$this->assertTrue($result);
}
public function testProcessWithAMixOfMatchingRules()
{
$userRule = $this->getIgnoreOriginUserRule('rule as string');
$anotherUserRule = $this->getIgnoreOriginUserRule('another rule as string');
$user = $this->getUser([$userRule, $anotherUserRule]);
$entry = new Entry($user);
$entry->setUrl('http://example.com/hello-world');
$this->ignoreOriginInstanceRuleRepository
->expects($this->once())
->method('findAll')
->willReturn([]);
$this->rulerz
->method('satisfies')
->will($this->onConsecutiveCalls(false, true));
$result = $this->processor->process($entry);
$this->assertTrue($result);
}
public function testProcessWithInstanceRules()
{
$user = $this->getUser();
$entry = new Entry($user);
$entry->setUrl('http://example.com/hello-world');
$instanceRule = $this->getIgnoreOriginInstanceRule('rule as string');
$this->ignoreOriginInstanceRuleRepository
->expects($this->once())
->method('findAll')
->willReturn([$instanceRule]);
$this->rulerz
->expects($this->once())
->method('satisfies')
->willReturn(true);
$result = $this->processor->process($entry);
$this->assertTrue($result);
}
public function testProcessWithMixedRules()
{
$userRule = $this->getIgnoreOriginUserRule('rule as string');
$user = $this->getUser([$userRule]);
$entry = new Entry($user);
$entry->setUrl('http://example.com/hello-world');
$instanceRule = $this->getIgnoreOriginInstanceRule('rule as string');
$this->ignoreOriginInstanceRuleRepository
->expects($this->once())
->method('findAll')
->willReturn([$instanceRule]);
$this->rulerz
->method('satisfies')
->will($this->onConsecutiveCalls(false, true));
$result = $this->processor->process($entry);
$this->assertTrue($result);
}
private function getUser(array $ignoreOriginRules = [])
{
$user = new User();
$config = new Config($user);
$user->setConfig($config);
foreach ($ignoreOriginRules as $rule) {
$config->addIgnoreOriginRule($rule);
}
return $user;
}
private function getIgnoreOriginUserRule($rule)
{
$ignoreOriginUserRule = new IgnoreOriginUserRule();
$ignoreOriginUserRule->setRule($rule);
return $ignoreOriginUserRule;
}
private function getIgnoreOriginInstanceRule($rule)
{
$ignoreOriginInstanceRule = new IgnoreOriginInstanceRule();
$ignoreOriginInstanceRule->setRule($rule);
return $ignoreOriginInstanceRule;
}
private function getRulerZMock()
{
return $this->getMockBuilder('RulerZ\RulerZ')
->disableOriginalConstructor()
->getMock();
}
private function getIgnoreOriginInstanceRuleRepositoryMock()
{
return $this->getMockBuilder('Wallabag\CoreBundle\Repository\IgnoreOriginInstanceRuleRepository')
->disableOriginalConstructor()
->getMock();
}
private function getLogger()
{
return new Logger('foo');
}
}