Skip to content

Commit

Permalink
Merge pull request #62 from synolia/feat/dynamic-value-mappings
Browse files Browse the repository at this point in the history
feat(anonymizer): allow expression language as dynamic value
  • Loading branch information
oallain committed Apr 4, 2024
2 parents 9ada78a + 3f7598e commit 2147fe4
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 16 deletions.
1 change: 1 addition & 0 deletions .github/workflows/analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,4 @@ jobs:
-
uses: symfonycorp/security-checker-action@v3
if: 'always() && steps.end-of-setup.outcome == ''success'''
continue-on-error: true
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,20 @@ Sylius\Component\Core\Model\Address: # Your class path
prefix: 'anonymized-'
```

Value can be null, an array, an int and a string
`value` can be null, an array, an int, a string and an [expression language](https://symfony.com/doc/current/reference/formats/expression_language.html)

Example of configuration with dynamic value
```yaml
Sylius\Component\Core\Model\Customer:
properties:
firstName:
value: '@="some-arbitrary-text..." ~ object.getId() ~ "...more-arbitrary-text"'
```

### Note:
> your expression language must starts with `@=` to be evaluated properly
> variable `object` is the current entity your are dealing with (eg. in that case `Sylius\Component\Core\Model\Customer`)
## Add form in advanced actions page

Expand Down
4 changes: 3 additions & 1 deletion src/Command/AnonymizeProcessCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
private function anonymizeEntityForClassName(string $className, bool $force, ?string $id = null): void
{
try {
$entity = $this->entityManager->getMetadataFactory()->getMetadataFor($className);
/** @var class-string $classString */
$classString = $className;
$entity = $this->entityManager->getMetadataFactory()->getMetadataFor($classString);
} catch (\Exception $exception) {
throw new \LogicException('Entity does not exist', 1, $exception);
}
Expand Down
56 changes: 43 additions & 13 deletions src/Provider/Anonymizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Faker\Factory;
use Faker\Generator;
use Psr\Log\LoggerInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
Expand Down Expand Up @@ -36,6 +37,8 @@ final class Anonymizer implements AnonymizerInterface

private PropertyAccessorInterface $propertyAccess;

private ExpressionLanguage $expressionLanguage;

public function __construct(
private LoaderChain $loaderChain,
private EventDispatcherInterface $eventDispatcher,
Expand All @@ -46,6 +49,7 @@ public function __construct(
->enableMagicCall()
->getPropertyAccessor()
;
$this->expressionLanguage = new ExpressionLanguage();
}

public function anonymize(Object $entity, bool $reset = false, int $maxRetries = 10000): void
Expand Down Expand Up @@ -116,19 +120,9 @@ private function anonymizeProcess(
$types = $propertyExtractor->getTypes($className, $propertyName);
$type = null !== $types ? $types[0]->getBuiltinType() : 'string';
$value = $attributeMetaData->getValue();
if (FakerOptionsValidator::DEFAULT_VALUE !== $value) {
if (is_array($value)) {
$this->setValue($entity, $propertyName, $type, $value);

return;
}

$this->setValue(
$entity,
$propertyName,
$type,
sprintf('%s%s', (string) $attributeMetaData->getPrefix(), (string) $value),
);
if ($this->isValueProvided($value)) {
$this->handleValue($entity, $value, $type, $propertyName, $attributeMetaData);

return;
}
Expand Down Expand Up @@ -170,7 +164,7 @@ private function anonymizeProcess(
$entity,
$propertyName,
$type,
is_array($value) ? $value : sprintf('%s%s', (string) $attributeMetaData->getPrefix(), (string) $value),
is_array($value) ? $value : sprintf('%s%s', $attributeMetaData->getPrefix(), $value),
);
}

Expand Down Expand Up @@ -284,4 +278,40 @@ private function isIterative(object $entity, string $className, string $property

return is_countable($entity->$getter());
}

private function isValueProvided(mixed $value): bool
{
return FakerOptionsValidator::DEFAULT_VALUE !== $value;
}

/** @param array|bool|int|string|null $value */
private function handleValue(
Object $entity,
$value,
string $type,
string $propertyName,
AttributeMetaDataInterface $attributeMetaData,
): void {
if (is_array($value)) {
$this->setValue($entity, $propertyName, $type, $value);

return;
}

if (\is_string($value) && str_starts_with($value, '@=')) {
$dynamicValue = substr($value, 2);
/** @var string $value */
$value = $this->expressionLanguage->evaluate($dynamicValue, ['object' => $entity]);
$this->setValue($entity, $propertyName, $type, $value);

return;
}

$this->setValue(
$entity,
$propertyName,
$type,
sprintf('%s%s', (string) $attributeMetaData->getPrefix(), (string) $value),
);
}
}
16 changes: 16 additions & 0 deletions tests/PHPUnit/Fixtures/YamlFoo.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

class YamlFoo
{
private int $id;

public $email = '';

public $value;
Expand All @@ -17,4 +19,18 @@ class YamlFoo
public $nullValue;

public $bar;

public $dynamicValue;

public function getId(): int
{
return $this->id;
}

public function setId(int $id): self
{
$this->id = $id;

return $this;
}
}
2 changes: 2 additions & 0 deletions tests/PHPUnit/Provider/AnonymizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function testYamlConfig(): void
$foo = new YamlFoo();
$foo->email = 'contact@synolia.com';
$foo->bar = 'coucou';
$foo->setId((new \DateTime())->getTimestamp());

$this->anonymizer->anonymize($foo, false, 10000);

Expand All @@ -59,5 +60,6 @@ public function testYamlConfig(): void
$this->assertSame('anonymize@synolia.com', $foo->email);
$this->assertStringContainsString('@', $foo->email);
$this->assertNull($foo->nullValue);
$this->assertSame('anonymized-' . $foo->getId() . '@domain+1.xyz', $foo->dynamicValue);
}
}
4 changes: 3 additions & 1 deletion tests/PHPUnit/config/mappings/anon1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ Tests\Synolia\SyliusGDPRPlugin\PHPUnit\Fixtures\YamlFoo:
value:
value: 'test-yaml-value'
nullValue:
value: null
value: null
dynamicValue:
value: '@="anonymized-" ~ object.getId() ~ "@domain+1.xyz"'

0 comments on commit 2147fe4

Please sign in to comment.