Skip to content

Commit

Permalink
Allow to exclude DSL methods/functions through attributes (#873)
Browse files Browse the repository at this point in the history
  • Loading branch information
norberttech committed Dec 8, 2023
1 parent ee7e107 commit 2f7564d
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ lib-parquet:
- any: ["src/lib/parquet/**/*"]
lib-dremel:
- any: ["src/lib/dremel/**/*"]
lib-rdsl:
- any: ["src/lib/rdsl/**/*"]
lib-snappy:
- any: ["src/lib/snappy/**/*"]

Expand Down
20 changes: 15 additions & 5 deletions src/core/etl/src/Flow/ETL/DataFrame.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
use Flow\RDSL\AccessControl\AllowAll;
use Flow\RDSL\AccessControl\AllowList;
use Flow\RDSL\AccessControl\DenyAll;
use Flow\RDSL\AccessControl\Except;
use Flow\RDSL\Attribute\DSLMethod;
use Flow\RDSL\Builder;
use Flow\RDSL\DSLNamespace;
use Flow\RDSL\Executor;
Expand Down Expand Up @@ -90,10 +90,7 @@ public static function fromJson(string $json) : self
new Finder(
$namespaces,
entryPointACL: new AllowList(['data_frame', 'df']),
methodACL: new Except(new AllowAll(), [
self::class . '::run',
self::class . '::fetch',
])
methodACL: new AllowAll()
)
);

Expand Down Expand Up @@ -208,6 +205,7 @@ public function collect() : self
*
* @lazy
*/
#[DSLMethod(exclude: true)]
public function collectRefs(References $references) : self
{
$this->transform(new CallbackRowTransformer(function (Row $row) use ($references) : Row {
Expand All @@ -225,6 +223,7 @@ public function collectRefs(References $references) : self
* @trigger
* Return total count of rows processed by this pipeline.
*/
#[DSLMethod(exclude: true)]
public function count() : int
{
$clone = clone $this;
Expand Down Expand Up @@ -257,6 +256,7 @@ public function crossJoin(self $dataFrame, string $prefix = '') : self
*
* @throws InvalidArgumentException
*/
#[DSLMethod(exclude: true)]
public function display(int $limit = 20, int|bool $truncate = 20, Formatter $formatter = new AsciiTableFormatter()) : string
{
return $formatter->format($this->fetch($limit), $truncate);
Expand Down Expand Up @@ -301,6 +301,7 @@ public function dropDuplicates(string|Reference ...$entries) : self
*
* @throws InvalidArgumentException
*/
#[DSLMethod(exclude: true)]
public function fetch(?int $limit = null) : Rows
{
$clone = clone $this;
Expand Down Expand Up @@ -365,6 +366,7 @@ public function filterPartitions(Partition\PartitionFilter|ScalarFunction $filte
*
* @param null|callable(Rows $rows) : void $callback
*/
#[DSLMethod(exclude: true)]
public function forEach(?callable $callback = null) : void
{
$clone = clone $this;
Expand All @@ -378,6 +380,7 @@ public function forEach(?callable $callback = null) : void
*
* @return \Generator<Rows>
*/
#[DSLMethod(exclude: true)]
public function get() : \Generator
{
$clone = clone $this;
Expand All @@ -392,6 +395,7 @@ public function get() : \Generator
*
* @return \Generator<array<array>>
*/
#[DSLMethod(exclude: true)]
public function getAsArray() : \Generator
{
$clone = clone $this;
Expand All @@ -408,6 +412,7 @@ public function getAsArray() : \Generator
*
* @return \Generator<Row>
*/
#[DSLMethod(exclude: true)]
public function getEach() : \Generator
{
$clone = clone $this;
Expand All @@ -426,6 +431,7 @@ public function getEach() : \Generator
*
* @return \Generator<array>
*/
#[DSLMethod(exclude: true)]
public function getEachAsArray() : \Generator
{
$clone = clone $this;
Expand Down Expand Up @@ -521,6 +527,7 @@ public function load(Loader $loader) : self
*
* @param callable(Row $row) : Row $callback
*/
#[DSLMethod(exclude: true)]
public function map(callable $callback) : self
{
$this->pipeline->add(new CallbackRowTransformer($callback));
Expand Down Expand Up @@ -604,6 +611,7 @@ public function pivot(Reference $ref) : self
/**
* @trigger
*/
#[DSLMethod(exclude: true)]
public function printRows(int|null $limit = 20, int|bool $truncate = 20, Formatter $formatter = new AsciiTableFormatter()) : void
{
$clone = clone $this;
Expand All @@ -620,6 +628,7 @@ public function printRows(int|null $limit = 20, int|bool $truncate = 20, Formatt
/**
* @trigger
*/
#[DSLMethod(exclude: true)]
public function printSchema(int|null $limit = 20, Schema\SchemaFormatter $formatter = new Schema\Formatter\ASCIISchemaFormatter()) : void
{
$clone = clone $this;
Expand Down Expand Up @@ -719,6 +728,7 @@ public function rows(Transformer|Transformation $transformer) : self
*
* @param null|callable(Rows $rows): void $callback
*/
#[DSLMethod(exclude: true)]
public function run(?callable $callback = null) : void
{
$clone = clone $this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function test_building_data_frame_from_json() : void
public function test_building_data_frame_from_json_with_forbidden_method_call() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("Method \"Flow\ETL\DataFrame::run\" is not allowed to be executed.");
$this->expectExceptionMessage("Method \"run\" from class \"Flow\ETL\DataFrame\" is excluded from DSL.");

DataFrame::fromJson(
<<<'JSON'
Expand Down
12 changes: 12 additions & 0 deletions src/lib/rdsl/src/Flow/RDSL/Attribute/DSL.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types=1);

namespace Flow\RDSL\Attribute;

#[\Attribute(\Attribute::TARGET_FUNCTION)]
final class DSL
{
public function __construct(public readonly bool $exclude = false)
{

}
}
12 changes: 12 additions & 0 deletions src/lib/rdsl/src/Flow/RDSL/Attribute/DSLMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types=1);

namespace Flow\RDSL\Attribute;

#[\Attribute(\Attribute::TARGET_METHOD)]
final class DSLMethod
{
public function __construct(public readonly bool $exclude = false)
{

}
}
38 changes: 35 additions & 3 deletions src/lib/rdsl/src/Flow/RDSL/Finder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Flow\RDSL;

use Flow\RDSL\AccessControl\DenyAll;
use Flow\RDSL\Attribute\DSL;
use Flow\RDSL\Attribute\DSLMethod;
use Flow\RDSL\Exception\InvalidArgumentException;

final class Finder
Expand Down Expand Up @@ -40,7 +42,7 @@ public function __construct(
public function findFunction(string $name, bool $entryPoint) : \ReflectionFunction
{
if (\function_exists($name)) {
$reflection = new \ReflectionFunction($name);
$reflection = $this->validateFunctionAttribute(new \ReflectionFunction($name));

if ($reflection->getNamespaceName() === '') {
foreach ($this->namespaces as $namespace) {
Expand Down Expand Up @@ -87,7 +89,7 @@ public function findFunction(string $name, bool $entryPoint) : \ReflectionFuncti
throw new InvalidArgumentException(\sprintf('Function "%s" from namespace "%s" is not allowed to be executed as an entry point.', $name, $namespace->name));
}

return new \ReflectionFunction($function);
return $this->validateFunctionAttribute(new \ReflectionFunction($function));
}
}

Expand All @@ -110,7 +112,7 @@ public function findMethod(string $class, string $method) : \ReflectionMethod
throw new InvalidArgumentException(\sprintf('Method "%s" does not exists in class "%s"', $method, $class));
}

$methodReflection = $classReflection->getMethod($method);
$methodReflection = $this->validateMethodAttribute($classReflection->getMethod($method));

$methodName = $classReflection->getName() . '::' . $method;

Expand All @@ -120,4 +122,34 @@ public function findMethod(string $class, string $method) : \ReflectionMethod

return $methodReflection;
}

private function validateFunctionAttribute(\ReflectionFunction $reflection) : \ReflectionFunction
{
$dslAttribute = $reflection->getAttributes(DSL::class);

if (\count($dslAttribute)) {
$dslAttribute = $dslAttribute[0]->newInstance();

if ($dslAttribute->exclude) {
throw new InvalidArgumentException(\sprintf('Function "%s" from namespace "%s" is excluded from DSL.', $reflection->getShortName(), $reflection->getNamespaceName()));
}
}

return $reflection;
}

private function validateMethodAttribute(\ReflectionMethod $reflection) : \ReflectionMethod
{
$dslAttribute = $reflection->getAttributes(DSLMethod::class);

if (\count($dslAttribute)) {
$dslAttribute = $dslAttribute[0]->newInstance();

if ($dslAttribute->exclude) {
throw new InvalidArgumentException(\sprintf('Method "%s" from class "%s" is excluded from DSL.', $reflection->getShortName(), $reflection->class));
}
}

return $reflection;
}
}
7 changes: 7 additions & 0 deletions src/lib/rdsl/tests/Flow/RDSL/Tests/Fixtures/IntObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Flow\RDSL\Tests\Fixtures;

use Flow\RDSL\Attribute\DSLMethod;

final class IntObject
{
private int $value = 0;
Expand All @@ -17,6 +19,11 @@ public function add(int|Literal $a) : self
return $this;
}

#[DSLMethod(exclude: true)]
public function excluded() : void
{
}

public function set(int|Literal $a) : self
{
$this->value = ($a instanceof Literal) ? $a->value : $a;
Expand Down
9 changes: 9 additions & 0 deletions src/lib/rdsl/tests/Flow/RDSL/Tests/Fixtures/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

namespace Flow\RDSL\Tests\Fixtures;

use Flow\RDSL\Attribute\DSL;

#[DSL]
function int(int $value) : IntObject
{
return (new IntObject())->set($value);
}

#[DSL]
function lit(mixed $value) : Literal
{
return new Literal($value);
}

#[DSL(exclude: true)]
function exclude() : void
{
}
27 changes: 27 additions & 0 deletions src/lib/rdsl/tests/Flow/RDSL/Tests/Unit/DSLFinderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,39 @@
use Flow\RDSL\DSLNamespace;
use Flow\RDSL\Exception\InvalidArgumentException;
use Flow\RDSL\Finder;
use Flow\RDSL\Tests\Fixtures\IntObject;
use PHPUnit\Framework\TestCase;

require_once __DIR__ . '/../Fixtures/functions.php';

final class DSLFinderTest extends TestCase
{
public function test_finding_functions_excluded_through_attributes() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("Function \"exclude\" from namespace \"Flow\RDSL\Tests\Fixtures\" is excluded from DSL.");

(new Finder(
[new DSLNamespace('\Flow\RDSL\Tests\Fixtures', new AllowAll())],
new AllowAll(),
new AllowAll()
))
->findFunction('exclude', false);
}

public function test_finding_methods_excluded_through_attributes() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("Method \"excluded\" from class \"Flow\RDSL\Tests\Fixtures\IntObject\" is excluded from DSL.");

(new Finder(
[new DSLNamespace('\Flow\RDSL\Tests\Fixtures', new AllowAll())],
new AllowAll(),
new AllowAll()
))
->findMethod(IntObject::class, 'excluded');
}

public function test_not_allowed_function_from_global_namespace_by_default() : void
{
$this->expectException(InvalidArgumentException::class);
Expand Down

0 comments on commit 2f7564d

Please sign in to comment.