Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: PHP Tests

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php-versions: ['8.3', '8.4', '8.5']

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: none

- name: Check PHP Version
run: php -v

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run unit tests
run: ./vendor/bin/phpunit -c phpunit.xml.dist
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/vendor/
/composer.lock
/.phpunit.cache/
.phpunit.result.cache
17 changes: 15 additions & 2 deletions Command/GetDefinitionCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Oliverde8\PhpEtlBundle\Command;

use Oliverde8\PhpEtlBundle\Etl\ChainDefinitionInterface\ChainDefinitionInterface;
use Oliverde8\PhpEtlBundle\Services\ChainProcessorsManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -36,7 +37,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$chainName = $input->getArgument("name");
$definition = $this->chainProcessorsManager->getRawDefinition($chainName);

echo Yaml::dump($definition, 4);
return 0;
if ($definition instanceof ChainDefinitionInterface) {
$output->writeln(sprintf(
'V2 (PHP) chain definition: %s (key: %s)',
$definition::class,
$definition->getKey()
));

return Command::SUCCESS;
}

// V1 (YAML) chains are stored as their raw YAML string; re-dump for consistent formatting.
$output->writeln(Yaml::dump(Yaml::parse($definition), 4));

return Command::SUCCESS;
}
}
2 changes: 1 addition & 1 deletion Etl/ChainDefinition/CleanupOldExecutionDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function getKey(): string

public function build(): ChainConfig
{
return new ChainConfig()
return (new ChainConfig())
->addLink(new FindOldExecutionConfig((new \DateTime())->modify('-1 month')))
->addLink(new DeleteEntityForOldExecutionConfig())
->addLink(new DeleteFilesForOldExecutionConfig());
Expand Down
2 changes: 2 additions & 0 deletions Etl/ChainDefinition/ExampleDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ public function build(): ChainConfig
singleElement: true
))
->addLink(new CsvFileWriterConfig('output.csv'));

return $chainConfig;
}
}
4 changes: 3 additions & 1 deletion EventSubscriber/EtlExecutionEventSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public function setChainDetails(BeforeEntityPersistedEvent $event): void
}

$definition = $this->chainProcessorManager->getRawDefinition($entity->getName());
$entity->setDefinition($definition);
// Legacy YAML definitions are strings; V2 definitions are objects - store the
// definition class so the entity's (string) "definition" column stays valid.
$entity->setDefinition(\is_string($definition) ? $definition : $definition::class);
$entity->setStatus(EtlExecution::STATUS_WAITING);
}

Expand Down
2 changes: 0 additions & 2 deletions Resources/config/parameters.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
parameters:
oliverde8-php-etl:
chains: []
oliverde8.php.etl.base_dir: "%kernel.project_dir%/var/etl"
oliverde8.php.etl.min_keep: "1 month"
50 changes: 50 additions & 0 deletions Tests/Etl/ChainDefinition/ChainDefinitionBuildTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Oliverde8\PhpEtlBundle\Tests\Etl\ChainDefinition;

use Oliverde8\Component\PhpEtl\ChainConfig;
use Oliverde8\PhpEtlBundle\Etl\ChainDefinition\CleanupOldExecutionDefinition;
use Oliverde8\PhpEtlBundle\Etl\ChainDefinition\ExampleDefinition;
use Oliverde8\PhpEtlBundle\Etl\ChainDefinition\FlysystemExampleDefinition;
use Oliverde8\PhpEtlBundle\Etl\ChainDefinitionInterface\ChainDefinitionInterface;
use PHPUnit\Framework\TestCase;

/**
* Guards the built-in V2 (PHP) chain definitions.
*
* Every definition must expose a non-empty key and its build() must return a
* ChainConfig. This is a cheap regression net for two mistakes that are easy to
* ship in a PHP-configured chain: forgetting to `return` from build() (fatals
* with a TypeError at execution time) and PHP-version-specific syntax that only
* parses on the developer's local version.
*/
class ChainDefinitionBuildTest extends TestCase
{
/**
* @return iterable<string, array{0: ChainDefinitionInterface}>
*/
public static function definitionProvider(): iterable
{
yield 'example' => [new ExampleDefinition()];
yield 'cleanup-old-execution' => [new CleanupOldExecutionDefinition()];
yield 'flysystem-example' => [new FlysystemExampleDefinition()];
}

/**
* @dataProvider definitionProvider
*/
public function testGetKeyIsNonEmptyString(ChainDefinitionInterface $definition): void
{
$this->assertNotEmpty($definition->getKey());
}

/**
* @dataProvider definitionProvider
*/
public function testBuildReturnsChainConfig(ChainDefinitionInterface $definition): void
{
$this->assertInstanceOf(ChainConfig::class, $definition->build());
}
}
15 changes: 10 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,24 @@
}
],
"require": {
"php": ">=8.2",
"oliverde8/php-etl": "^v1.2.0-alpha5|^v2.0.0-alpha5",
"symfony/framework-bundle": "^7.0|^8.0",
"symfony/messenger": "^7.0|^8.0",
"php": ">=8.3",
"oliverde8/php-etl": "^2.0",
"symfony/framework-bundle": "^7.4|^8.0",
"symfony/messenger": "^7.4|^8.0",
"ext-json": "*"
},
"autoload": {
"psr-4": {
"Oliverde8\\PhpEtlBundle\\": "."
}
},
"suggest": {
"easycorp/easyadmin-bundle": "To manage and run ETL executions from an admin UI (see oliverde8/php-etl-easyadmin-bundle); enables the EtlExecution admin event subscriber",
"league/flysystem": "To find and process files from external/remote filesystems (ExternalFileFinder / FlysystemExternalFileFinderCompiler)"
},
"require-dev": {
"symfony/monolog-bundle": "^3.7",
"symfony/console": "^7.0|^8.0"
"symfony/console": "^7.4|^8.0",
"phpunit/phpunit": "^11.0"
}
}
25 changes: 25 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache"
>
<testsuites>
<testsuite name="php-etl-bundle">
<directory>Tests</directory>
</testsuite>
</testsuites>

<source>
<include>
<directory>.</directory>
</include>
<exclude>
<directory>Tests</directory>
<directory>vendor</directory>
<directory>Resources</directory>
</exclude>
</source>
</phpunit>
Loading