Skip to content

Commit

Permalink
Merge pull request #98 from php-etl/feature/filtering-plugin
Browse files Browse the repository at this point in the history
Added the filtering plugin
  • Loading branch information
gplanchat authored Apr 25, 2023
2 parents ae0c097 + 5291cf1 commit fe598fe
Show file tree
Hide file tree
Showing 12 changed files with 815 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@
"Kiboko\\Component\\Satellite\\Plugin\\Stream\\Service",
"Kiboko\\Component\\Satellite\\Plugin\\SFTP\\Service",
"Kiboko\\Component\\Satellite\\Plugin\\FTP\\Service",
"Kiboko\\Component\\Satellite\\Plugin\\Batching\\Service"
"Kiboko\\Component\\Satellite\\Plugin\\Batching\\Service",
"Kiboko\\Component\\Satellite\\Plugin\\Filtering\\Service"
]
}
},
Expand Down
163 changes: 163 additions & 0 deletions src/Plugin/Filtering/Builder/Drop.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

declare(strict_types=1);

namespace Kiboko\Component\Satellite\Plugin\Filtering\Builder;

use Kiboko\Component\Bucket\AcceptanceResultBucket;
use Kiboko\Component\Bucket\RejectionResultBucket;
use Kiboko\Contract\Configurator\StepBuilderInterface;
use Kiboko\Contract\Pipeline\TransformerInterface;
use PhpParser\Builder;
use PhpParser\Node;

final class Drop implements StepBuilderInterface
{
private ?Node\Expr $logger = null;
private ?Node\Expr $rejection = null;
private ?Node\Expr $state = null;
/** @var list<?Node\Expr> */
private array $exclusions = [];

public function __construct()
{
}

public function withLogger(Node\Expr $logger): self
{
$this->logger = $logger;

return $this;
}

public function withRejection(Node\Expr $rejection): self
{
$this->rejection = $rejection;

return $this;
}

public function withState(Node\Expr $state): self
{
$this->state = $state;

return $this;
}

public function withExclusions(Node\Expr ...$exclusions): self
{
array_push($this->exclusions, ...$exclusions);

return $this;
}

private function buildExclusions(Node\Expr ...$exclusions): Node\Expr
{
if (\count($exclusions) > 3) {
$length = \count($exclusions);
$middle = (int) floor($length / 2);
$left = \array_slice($exclusions, 0, $middle);
$right = \array_slice($exclusions, $middle, $length);

return new Node\Expr\BinaryOp\BooleanAnd(
$this->buildExclusions(...$left),
$this->buildExclusions(...$right),
);
}

if (\count($exclusions) > 2) {
$right = array_shift($exclusions);

return new Node\Expr\BinaryOp\BooleanAnd(
$this->buildExclusions(...$exclusions),
$right,
);
}

if (\count($exclusions) > 1) {
$left = array_pop($exclusions);
$right = array_pop($exclusions);

return new Node\Expr\BinaryOp\BooleanAnd(
$left,
$right,
);
}

if (\count($exclusions) > 0) {
return array_pop($exclusions);
}

return new Node\Expr\ConstFetch(
new Node\Name('false'),
);
}

public function getNode(): Node
{
return new Node\Expr\New_(
class: new Node\Stmt\Class_(null, [
'implements' => [
new Node\Name\FullyQualified(TransformerInterface::class),
],
'stmts' => [
(new Builder\Method('transform'))
->makePublic()
->setReturnType(new Node\Name\FullyQualified(\Generator::class))
->addStmts([
new Node\Stmt\Expression(
new Node\Expr\Assign(
new Node\Expr\Variable('input'),
new Node\Expr\Yield_(),
)
),
new Node\Stmt\While_(
new Node\Expr\ConstFetch(
new Node\Name('true'),
),
[
new Node\Stmt\If_(
$this->buildExclusions(...$this->exclusions),
[
'stmts' => [
new Node\Stmt\Expression(
new Node\Expr\Assign(
new Node\Expr\Variable('input'),
new Node\Expr\Yield_(
new Node\Expr\New_(
new Node\Name\FullyQualified(RejectionResultBucket::class),
),
),
),
),
new Node\Stmt\Continue_(),
],
]
),
new Node\Stmt\Expression(
new Node\Expr\Assign(
new Node\Expr\Variable('input'),
new Node\Expr\Yield_(
new Node\Expr\New_(
new Node\Name\FullyQualified(AcceptanceResultBucket::class),
[
new Node\Arg(new Node\Expr\Variable('input')),
]
),
),
),
),
],
),
new Node\Stmt\Expression(
new Node\Expr\Yield_(
new Node\Expr\Variable('input')
),
),
])
->getNode(),
],
])
);
}
}
166 changes: 166 additions & 0 deletions src/Plugin/Filtering/Builder/Reject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

declare(strict_types=1);

namespace Kiboko\Component\Satellite\Plugin\Filtering\Builder;

use Kiboko\Component\Bucket\AcceptanceResultBucket;
use Kiboko\Component\Bucket\RejectionResultBucket;
use Kiboko\Contract\Configurator\StepBuilderInterface;
use Kiboko\Contract\Pipeline\TransformerInterface;
use PhpParser\Builder;
use PhpParser\Node;

final class Reject implements StepBuilderInterface
{
private ?Node\Expr $logger = null;
private ?Node\Expr $rejection = null;
private ?Node\Expr $state = null;
/** @var list<?Node\Expr> */
private array $exclusions = [];

public function __construct()
{
}

public function withLogger(Node\Expr $logger): self
{
$this->logger = $logger;

return $this;
}

public function withRejection(Node\Expr $rejection): self
{
$this->rejection = $rejection;

return $this;
}

public function withState(Node\Expr $state): self
{
$this->state = $state;

return $this;
}

public function withExclusions(Node\Expr ...$exclusions): self
{
array_push($this->exclusions, ...$exclusions);

return $this;
}

private function buildExclusions(Node\Expr ...$exclusions): Node\Expr
{
if (\count($exclusions) > 3) {
$length = \count($exclusions);
$middle = (int) floor($length / 2);
$left = \array_slice($exclusions, 0, $middle);
$right = \array_slice($exclusions, $middle, $length);

return new Node\Expr\BinaryOp\BooleanAnd(
$this->buildExclusions(...$left),
$this->buildExclusions(...$right),
);
}

if (\count($exclusions) > 2) {
$right = array_shift($exclusions);

return new Node\Expr\BinaryOp\BooleanAnd(
$this->buildExclusions(...$exclusions),
$right,
);
}

if (\count($exclusions) > 1) {
$left = array_pop($exclusions);
$right = array_pop($exclusions);

return new Node\Expr\BinaryOp\BooleanAnd(
$left,
$right,
);
}

if (\count($exclusions) > 0) {
return array_pop($exclusions);
}

return new Node\Expr\ConstFetch(
new Node\Name('false'),
);
}

public function getNode(): Node
{
return new Node\Expr\New_(
class: new Node\Stmt\Class_(null, [
'implements' => [
new Node\Name\FullyQualified(TransformerInterface::class),
],
'stmts' => [
(new Builder\Method('transform'))
->makePublic()
->setReturnType(new Node\Name\FullyQualified(\Generator::class))
->addStmts([
new Node\Stmt\Expression(
new Node\Expr\Assign(
new Node\Expr\Variable('input'),
new Node\Expr\Yield_(),
)
),
new Node\Stmt\While_(
new Node\Expr\ConstFetch(
new Node\Name('true'),
),
[
new Node\Stmt\If_(
$this->buildExclusions(...$this->exclusions),
[
'stmts' => [
new Node\Stmt\Expression(
new Node\Expr\Assign(
new Node\Expr\Variable('input'),
new Node\Expr\Yield_(
new Node\Expr\New_(
new Node\Name\FullyQualified(RejectionResultBucket::class),
[
new Node\Arg(new Node\Expr\Variable('input')),
]
),
),
),
),
new Node\Stmt\Continue_(),
],
]
),
new Node\Stmt\Expression(
new Node\Expr\Assign(
new Node\Expr\Variable('input'),
new Node\Expr\Yield_(
new Node\Expr\New_(
new Node\Name\FullyQualified(AcceptanceResultBucket::class),
[
new Node\Arg(new Node\Expr\Variable('input')),
]
),
),
),
),
],
),
new Node\Stmt\Expression(
new Node\Expr\Yield_(
new Node\Expr\Variable('input')
),
),
])
->getNode(),
],
])
);
}
}
57 changes: 57 additions & 0 deletions src/Plugin/Filtering/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Kiboko\Component\Satellite\Plugin\Filtering;

use Kiboko\Contract\Configurator\PluginConfigurationInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;

use function Kiboko\Component\SatelliteToolbox\Configuration\mutuallyExclusiveFields;

final class Configuration implements PluginConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$reject = new Configuration\Reject();
$drop = new Configuration\Drop();

$builder = new TreeBuilder('filter');

/* @phpstan-ignore-next-line */
$builder->getRootNode()
->validate()
->always($this->cleanupFields('reject', 'drop'))
->end()
->validate()
->always(mutuallyExclusiveFields('reject', 'drop'))
->end()
->children()
->arrayNode('expression_language')
->scalarPrototype()->end()
->end()
->append(node: $reject->getConfigTreeBuilder()->getRootNode())
->append(node: $drop->getConfigTreeBuilder()->getRootNode())
->end()
;

return $builder;
}

private function cleanupFields(string ...$fieldNames): \Closure
{
return function (array $value) use ($fieldNames) {
foreach ($fieldNames as $fieldName) {
if (!\array_key_exists($fieldName, $value)) {
continue;
}

if (!\is_array($value[$fieldName]) || \count($value[$fieldName]) <= 0) {
unset($value[$fieldName]);
}
}

return $value;
};
}
}
Loading

0 comments on commit fe598fe

Please sign in to comment.