Skip to content

Commit 0105d37

Browse files
committed
invalidate export on save personal data
1 parent 3361b1e commit 0105d37

File tree

9 files changed

+311
-14
lines changed

9 files changed

+311
-14
lines changed

Api/ExportEntityManagementInterface.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Opengento\Gdpr\Api;
99

1010
use Magento\Framework\Exception\AlreadyExistsException;
11+
use Magento\Framework\Exception\CouldNotDeleteException;
1112
use Magento\Framework\Exception\CouldNotSaveException;
1213
use Magento\Framework\Exception\LocalizedException;
1314
use Opengento\Gdpr\Api\Data\ExportEntityInterface;
@@ -39,4 +40,16 @@ public function create(int $entityId, string $entityType, ?string $fileName = nu
3940
* @throws LocalizedException
4041
*/
4142
public function export(ExportEntityInterface $exportEntity): ExportEntityInterface;
43+
44+
/**
45+
* Invalidate the export entity and create a new one to process
46+
*
47+
* @param ExportEntityInterface $exportEntity
48+
* @return ExportEntityInterface
49+
* @throws AlreadyExistsException
50+
* @throws CouldNotDeleteException
51+
* @throws CouldNotSaveException
52+
* @throws LocalizedException
53+
*/
54+
public function invalidate(ExportEntityInterface $exportEntity): ExportEntityInterface;
4255
}

Cron/EraseEntityScheduler.php

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
use Exception;
1212
use Magento\Framework\Api\FilterBuilder;
1313
use Magento\Framework\App\Config\ScopeConfigInterface;
14+
use Magento\Framework\Exception\CouldNotSaveException;
15+
use Magento\Framework\Exception\LocalizedException;
1416
use Magento\Store\Model\ScopeInterface;
1517
use Opengento\Gdpr\Model\Config;
18+
use Opengento\Gdpr\Model\Entity\EntityTypeList;
1619
use Opengento\Gdpr\Model\Erase\EraseEntityScheduler as EraseEntitySchedulerService;
1720
use Psr\Log\LoggerInterface;
1821

@@ -49,40 +52,50 @@ final class EraseEntityScheduler
4952
private $filterBuilder;
5053

5154
/**
52-
* @var string[]
55+
* @var EntityTypeList
5356
*/
54-
private $entityTypes;
57+
private $entityTypeList;
5558

5659
public function __construct(
5760
LoggerInterface $logger,
5861
ScopeConfigInterface $scopeConfig,
5962
Config $config,
6063
EraseEntitySchedulerService $eraseEntityScheduler,
6164
FilterBuilder $filterBuilder,
62-
array $entityTypes
65+
EntityTypeList $entityTypeList
6366
) {
6467
$this->logger = $logger;
6568
$this->scopeConfig = $scopeConfig;
6669
$this->config = $config;
6770
$this->eraseEntityScheduler = $eraseEntityScheduler;
6871
$this->filterBuilder = $filterBuilder;
69-
$this->entityTypes = $entityTypes;
72+
$this->entityTypeList = $entityTypeList;
7073
}
7174

7275
public function execute(): void
7376
{
7477
if ($this->config->isModuleEnabled() && $this->config->isErasureEnabled()) {
7578
try {
76-
$this->filterBuilder->setField('created_at');
77-
$this->filterBuilder->setValue(new DateTime('-' . $this->resolveErasureMaxAge() . 'days'));
78-
$this->filterBuilder->setConditionType('lteq');
79-
$this->eraseEntityScheduler->schedule($this->entityTypes, $this->filterBuilder->create());
79+
$this->scheduleEntitiesErasure();
8080
} catch (Exception $e) {
8181
$this->logger->error($e->getMessage(), $e->getTrace());
8282
}
8383
}
8484
}
8585

86+
/**
87+
* @throws CouldNotSaveException
88+
* @throws LocalizedException
89+
* @throws Exception
90+
*/
91+
private function scheduleEntitiesErasure(): void
92+
{
93+
$this->filterBuilder->setField('created_at');
94+
$this->filterBuilder->setValue(new DateTime('-' . $this->resolveErasureMaxAge() . 'days'));
95+
$this->filterBuilder->setConditionType('lteq');
96+
$this->eraseEntityScheduler->schedule($this->entityTypeList->getEntityTypes(), $this->filterBuilder->create());
97+
}
98+
8699
private function resolveErasureMaxAge(): int
87100
{
88101
return (int) $this->scopeConfig->getValue(self::CONFIG_PATH_ERASURE_MAX_AGE, ScopeInterface::SCOPE_STORE);

Model/Config/Source/EraseComponents.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private function retrieveDelegateProcessors(): array
6464
$delegateProcessors[] = $this->retrieveArgument($resolver, 'processors');
6565
}
6666

67-
return array_keys(array_merge(...$delegateProcessors));
67+
return array_keys(array_merge([], ...$delegateProcessors));
6868
}
6969

7070
/**

Model/Entity/EntityTypeList.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
/**
3+
* Copyright © OpenGento, All rights reserved.
4+
* See LICENSE bundled with this library for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Opengento\Gdpr\Model\Entity;
9+
10+
use function array_keys;
11+
use function array_merge;
12+
13+
/**
14+
* @api
15+
*/
16+
final class EntityTypeList
17+
{
18+
/**
19+
* @var string[][]
20+
*/
21+
private $list;
22+
23+
/**
24+
* @var string[]|null
25+
*/
26+
private $entityTypes;
27+
28+
public function __construct(
29+
array $list
30+
) {
31+
$this->list = $list;
32+
}
33+
34+
public function getList(): array
35+
{
36+
return $this->list;
37+
}
38+
39+
public function getEntityTypes(): array
40+
{
41+
$entityTypes = [];
42+
43+
foreach ($this->getList() as $entity => $types) {
44+
$entityTypes[] = array_keys($types);
45+
}
46+
47+
return $this->entityTypes ?? $this->entityTypes = array_merge([], ...$entityTypes);
48+
}
49+
}

Model/Entity/EntityTypeResolver.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* Copyright © OpenGento, All rights reserved.
4+
* See LICENSE bundled with this library for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Opengento\Gdpr\Model\Entity;
9+
10+
use Exception;
11+
use Magento\Framework\EntityManager\TypeResolver;
12+
13+
/**
14+
* @api
15+
*/
16+
final class EntityTypeResolver
17+
{
18+
/**
19+
* @var TypeResolver
20+
*/
21+
private $typeResolver;
22+
23+
/**
24+
* @var EntityTypeList
25+
*/
26+
private $entityTypeList;
27+
28+
public function __construct(
29+
TypeResolver $typeResolver,
30+
EntityTypeList $entityTypeList
31+
) {
32+
$this->typeResolver = $typeResolver;
33+
$this->entityTypeList = $entityTypeList;
34+
}
35+
36+
/**
37+
* @param object $entity
38+
* @return string[]
39+
* @throws Exception
40+
*/
41+
public function resolve(object $entity): array
42+
{
43+
return $this->entityTypeList->getList()[$this->typeResolver->resolve($entity)] ?? [];
44+
}
45+
}

Model/ExportEntityManagement.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ public function export(ExportEntityInterface $exportEntity): ExportEntityInterfa
103103
return $exportEntity;
104104
}
105105

106+
public function invalidate(ExportEntityInterface $exportEntity): ExportEntityInterface
107+
{
108+
$this->exportRepository->delete($exportEntity);
109+
110+
return $this->create($exportEntity->getEntityId(), $exportEntity->getEntityType(), $exportEntity->getFileName());
111+
}
112+
106113
private function resolveDefaultFileName(): string
107114
{
108115
return (string) $this->scopeConfig->getValue(self::CONFIG_PATH_EXPORT_FILE_NAME, ScopeInterface::SCOPE_STORE);

Observer/InvalidateExport.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
/**
3+
* Copyright © OpenGento, All rights reserved.
4+
* See LICENSE bundled with this library for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Opengento\Gdpr\Observer;
9+
10+
use Exception;
11+
use Magento\Framework\Api\Filter;
12+
use Magento\Framework\Api\FilterBuilder;
13+
use Magento\Framework\Api\SearchCriteriaBuilder;
14+
use Magento\Framework\Api\SearchResultsInterface;
15+
use Magento\Framework\Event\Observer;
16+
use Magento\Framework\Event\ObserverInterface;
17+
use Magento\Framework\Exception\LocalizedException;
18+
use Magento\Framework\Model\AbstractModel;
19+
use Opengento\Gdpr\Api\Data\ExportEntityInterface;
20+
use Opengento\Gdpr\Api\Data\ExportEntitySearchResultsInterface;
21+
use Opengento\Gdpr\Api\ExportEntityManagementInterface;
22+
use Opengento\Gdpr\Api\ExportEntityRepositoryInterface;
23+
use Opengento\Gdpr\Model\Entity\EntityTypeResolver;
24+
use Psr\Log\LoggerInterface;
25+
26+
final class InvalidateExport implements ObserverInterface
27+
{
28+
/**
29+
* @var ExportEntityRepositoryInterface
30+
*/
31+
private $exportRepository;
32+
33+
/**
34+
* @var ExportEntityManagementInterface
35+
*/
36+
private $exportManagement;
37+
38+
/**
39+
* @var SearchCriteriaBuilder
40+
*/
41+
private $criteriaBuilder;
42+
43+
/**
44+
* @var FilterBuilder
45+
*/
46+
private $filterBuilder;
47+
48+
/**
49+
* @var EntityTypeResolver
50+
*/
51+
private $entityTypeResolver;
52+
53+
/**
54+
* @var LoggerInterface
55+
*/
56+
private $logger;
57+
58+
public function __construct(
59+
ExportEntityRepositoryInterface $exportRepository,
60+
ExportEntityManagementInterface $exportManagement,
61+
SearchCriteriaBuilder $criteriaBuilder,
62+
FilterBuilder $filterBuilder,
63+
EntityTypeResolver $entityTypeResolver,
64+
LoggerInterface $logger
65+
) {
66+
$this->exportRepository = $exportRepository;
67+
$this->exportManagement = $exportManagement;
68+
$this->criteriaBuilder = $criteriaBuilder;
69+
$this->filterBuilder = $filterBuilder;
70+
$this->entityTypeResolver = $entityTypeResolver;
71+
$this->logger = $logger;
72+
}
73+
74+
public function execute(Observer $observer): void
75+
{
76+
/** @var AbstractModel $entity */
77+
$entity = $observer->getData('data_object');
78+
79+
try {
80+
foreach ($this->fetchExportEntities($entity)->getItems() as $exportEntity) {
81+
$this->exportManagement->invalidate($exportEntity);
82+
}
83+
} catch (LocalizedException $e) {
84+
$this->logger->error($e->getLogMessage(), $e->getTrace());
85+
} catch (Exception $e) {
86+
$this->logger->error($e->getMessage(), $e->getTrace());
87+
}
88+
}
89+
90+
/**
91+
* @param AbstractModel $entity
92+
* @return ExportEntitySearchResultsInterface
93+
* @throws LocalizedException
94+
* @throws Exception
95+
*/
96+
private function fetchExportEntities(AbstractModel $entity): SearchResultsInterface
97+
{
98+
$entityTypes = $this->entityTypeResolver->resolve($entity);
99+
100+
foreach ($entityTypes as $entityType => $idFieldName) {
101+
$this->criteriaBuilder->addFilters([
102+
$this->createEntityIdFilter((int) $entity->getData($idFieldName)),
103+
$this->createEntityTypeFilter($entityType)
104+
]);
105+
}
106+
107+
return $this->exportRepository->getList($this->criteriaBuilder->create());
108+
}
109+
110+
private function createEntityIdFilter(int $entityId): Filter
111+
{
112+
$this->filterBuilder->setField(ExportEntityInterface::ENTITY_ID);
113+
$this->filterBuilder->setValue($entityId);
114+
$this->filterBuilder->setConditionType('eq');
115+
116+
return $this->filterBuilder->create();
117+
}
118+
119+
private function createEntityTypeFilter(string $entityType): Filter
120+
{
121+
$this->filterBuilder->setField(ExportEntityInterface::ENTITY_TYPE);
122+
$this->filterBuilder->setValue($entityType);
123+
$this->filterBuilder->setConditionType('eq');
124+
125+
return $this->filterBuilder->create();
126+
}
127+
}

etc/di.xml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -890,12 +890,25 @@
890890
</argument>
891891
</arguments>
892892
</type>
893-
<!-- ToDo: Improve registration of the entity types -->
894-
<type name="Opengento\Gdpr\Cron\EraseEntityScheduler">
893+
<type name="Opengento\Gdpr\Model\Entity\EntityTypeList">
895894
<arguments>
896-
<argument name="entityTypes" xsi:type="array">
897-
<item name="customer" xsi:type="string">customer</item>
898-
<item name="guest" xsi:type="string">order</item>
895+
<argument name="list" xsi:type="array">
896+
<item name="Magento\Customer\Model\Customer" xsi:type="array">
897+
<item name="customer" xsi:type="string">entity_id</item>
898+
</item>
899+
<item name="Magento\Customer\Api\Data\AddressInterface" xsi:type="array">
900+
<item name="customer" xsi:type="string">customer_id</item>
901+
</item>
902+
<item name="Magento\Newsletter\Model\Subscriber" xsi:type="array">
903+
<item name="customer" xsi:type="string">customer_id</item>
904+
</item>
905+
<item name="Magento\Quote\Api\Data\CartInterface" xsi:type="array">
906+
<item name="customer" xsi:type="string">customer_id</item>
907+
</item>
908+
<item name="Magento\Sales\Api\Data\OrderInterface" xsi:type="array">
909+
<item name="customer" xsi:type="string">customer_id</item>
910+
<item name="order" xsi:type="string">entity_id</item>
911+
</item>
899912
</argument>
900913
</arguments>
901914
</type>

0 commit comments

Comments
 (0)