Skip to content

Commit

Permalink
MDTT-12: Support custom transformation plugins (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
subhojit-axl authored Apr 12, 2022
1 parent 81b57d2 commit 0777ebc
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 9 deletions.
53 changes: 50 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ composer require --dev axelerant/mdtt
Basically you follow these steps:

1. Specify test specifications
1. Specify test definitions
1. Run the tests
2. Specify test definitions
3. _Optionally_, specify transform plugins
4. Run the tests

You can find the basic template for the tool usage [here](https://github.com/axelerant/mdtt-usage).

### Test specification

Expand Down Expand Up @@ -113,7 +116,51 @@ tests:
destinationField: email
```
You can find the basic template for the tool usage [here](https://github.com/axelerant/mdtt-usage).
### Transform plugin
There could be a scenario where instead of directly storing the data from source, it must be transformed in some way (say, whitespaces must be stripped) before storing it in the destination database. A QA engineer can write their own plugin, to validate the business logic that does the transformation.
The test case would look like this:
```yaml
tests:
-
sourceField: name
transform: trim
destinationField: name
```
The QA engineer must specify the plugin class inside `tests/mdtt/src/Plugin/Transform`. The file name (and, class name) must be same as the plugin name mentioned in the test case with the first character in upper case, i.e. `Trim`. The plugin class must implement `\Mdtt\Transform\Transform` interface.

```php
<?php
// File location: tests/mdtt/src/Plugin/Transform/Trim.php.
class Trim implements \Mdtt\Transform\Transform
{
/**
* @inheritDoc
*/
public function name(): string
{
// Specifies the plugin name.
// This must be unique, if you have multiple plugin definitions.
// The same plugin name must be mentioned in the test case.
return "trim";
}
/**
* @inheritDoc
*/
public function process(mixed $data): mixed
{
// The logic that does the transformation.
return trim($data);
}
}
```

## Run tests

Expand Down
5 changes: 5 additions & 0 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ services:

Psr\Log\LoggerInterface: '@logger'

transform_plugin_manager:
class: \Mdtt\Transform\DefaultPluginManager

Mdtt\Transform\PluginManager: '@transform_plugin_manager'

Mdtt\:
lazy: true
resource: '../src/*'
Expand Down
30 changes: 27 additions & 3 deletions src/LoadDefinition/DefaultLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Mdtt\Definition\Validate\DataSource\Validator;
use Mdtt\Exception\SetupException;
use Mdtt\Test\DefaultTest;
use Mdtt\Transform\PluginManager;
use Mdtt\Transform\Transform;
use Psr\Log\LoggerInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Yaml\Yaml;
Expand All @@ -16,11 +18,17 @@ class DefaultLoader implements Load
{
private LoggerInterface $logger;
private Validator $dataSourceValidator;
private PluginManager $transformPluginManager;
/**
* @var array<string, Transform>
*/
private array $transformPlugins;

public function __construct(LoggerInterface $logger, Validator $validator)
public function __construct(LoggerInterface $logger, Validator $validator, PluginManager $transformPluginManager)
{
$this->logger = $logger;
$this->dataSourceValidator = $validator;
$this->transformPluginManager = $transformPluginManager;
}

/**
Expand Down Expand Up @@ -93,8 +101,24 @@ public function validate(): iterable
$sourceField = $test['sourceField'];
/** @var string $destinationField */
$destinationField = $test['destinationField'];

$parsedTests[] = new DefaultTest($sourceField, $destinationField, $this->logger);
$testInstance = new DefaultTest(
$sourceField,
$destinationField,
$this->logger
);

if (isset($test['transform'])) {
if (!isset($this->transformPlugins[$test['transform']])) {
$transformPlugin = $this->transformPluginManager->loadById($test['transform']);
$this->transformPlugins[$transformPlugin->name()] = $transformPlugin;
} else {
$transformPlugin = $this->transformPlugins[$test['transform']];
}

$testInstance->setTransform($transformPlugin);
}

$parsedTests[] = $testInstance;
}
$parsedTestDefinition->setTests($parsedTests);

Expand Down
15 changes: 14 additions & 1 deletion src/Test/DefaultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,22 @@ public function execute(array $sourceData, array $destinationData): void
$destinationData[$this->getDestinationField()]
));

/** @var string|int $sourceValue */
$sourceValue = $sourceData[$this->getSourceField()];
if ($this->getTransform() !== null) {
$sourceValue = $this->getTransform()->process($sourceValue);

$this->getLogger()->notice(sprintf(
"Applied transform: %s on source. Comparing source %s with destination %s",
$this->getTransform()->name(),
$sourceValue,
$destinationData[$this->getDestinationField()]
));
}

try {
Assert::assertSame(
$sourceData[$this->getSourceField()],
$sourceValue,
$destinationData[$this->getDestinationField()],
"Source and destination does not match."
);
Expand Down
28 changes: 26 additions & 2 deletions src/Test/Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

namespace Mdtt\Test;

use Mdtt\Transform\Transform;
use Psr\Log\LoggerInterface;

abstract class Test
{
private string $sourceField;
private string $destinationField;
private LoggerInterface $logger;
private ?Transform $transform;

/**
* @return \Psr\Log\LoggerInterface
Expand All @@ -24,12 +26,18 @@ public function getLogger(): LoggerInterface
* @param string $sourceField
* @param string $destinationField
* @param \Psr\Log\LoggerInterface $logger
* @param \Mdtt\Transform\Transform|null $transform
*/
public function __construct(string $sourceField, string $destinationField, LoggerInterface $logger)
{
public function __construct(
string $sourceField,
string $destinationField,
LoggerInterface $logger,
Transform $transform = null
) {
$this->sourceField = $sourceField;
$this->destinationField = $destinationField;
$this->logger = $logger;
$this->transform = $transform;
}

/**
Expand All @@ -48,6 +56,22 @@ public function getDestinationField(): string
return $this->destinationField;
}

/**
* @return \Mdtt\Transform\Transform|null
*/
public function getTransform(): ?Transform
{
return $this->transform;
}

/**
* @param \Mdtt\Transform\Transform $transform
*/
public function setTransform(Transform $transform): void
{
$this->transform = $transform;
}

/**
* Compare the source and the destination data.
*
Expand Down
22 changes: 22 additions & 0 deletions src/Transform/DefaultPluginManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Mdtt\Transform;

class DefaultPluginManager implements PluginManager
{
/**
* @inheritDoc
*/
public function loadById(string $id): Transform
{
$pluginId = ucwords($id);

require "tests/mdtt/src/Plugin/Transform/$pluginId.php";

/** @var \Mdtt\Transform\Transform $pluginInstance */
$pluginInstance = new $pluginId();
return $pluginInstance;
}
}
15 changes: 15 additions & 0 deletions src/Transform/PluginManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Mdtt\Transform;

interface PluginManager
{
/**
* Scans all transform plugins and instantiates them.
*
* @param string $id
*
* @return \Mdtt\Transform\Transform
*/
public function loadById(string $id): Transform;
}
24 changes: 24 additions & 0 deletions src/Transform/Transform.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Mdtt\Transform;

interface Transform
{
/**
* Name of the plugin.
*
* @return string
*/
public function name(): string;

/**
* Alters the provided data.
*
* @param string|int $data
*
* @return string|int
*/
public function process(mixed $data): mixed;
}

0 comments on commit 0777ebc

Please sign in to comment.