Skip to content
Snippets Groups Projects
Commit f7af5eb3 authored by Ilias Dimopoulos's avatar Ilias Dimopoulos Committed by Claudiu Cristea
Browse files

Issue #3478179 by idimopoulos, claudiu.cristea: Allow to check for the...

Issue #3478179 by idimopoulos, claudiu.cristea: Allow to check for the publication status before syncing
parent d5c94d7d
No related branches found
No related tags found
1 merge request!26Handle publication status of entities that have a published flag.
Pipeline #299938 passed with warnings
Showing
with 235 additions and 12 deletions
...@@ -223,6 +223,23 @@ curl http://example.com/node/123?_format=jsonld ...@@ -223,6 +223,23 @@ curl http://example.com/node/123?_format=jsonld
curl http://example.com/node/123?_format=turtle curl http://example.com/node/123?_format=turtle
``` ```
## Events
The module provides a set of events that can be used to alter the data and
entities before and after synchronization. The events are:
* `\Drupal\rdf_sync\Event\RdfSyncNormalizeEvent`: Allows to alter the triples
before they are serialized or add new ones.
* `\Drupal\rdf_sync\Event\RdfSyncEvent`: Allows to perform alterations on the
array of entities before syncing them.
## Sub-modules
The `rdf_sync_published` submodule provides a way to filter out unpublished
entities from synchronization. It does so by listening to the
`\Drupal\rdf_sync\Event\RdfSyncEvent` event and filtering out the entities that
are not published.
## Contributing ## Contributing
Feature requests, bug reports, and merge requests are welcomed. Please follow Feature requests, bug reports, and merge requests are welcomed. Please follow
......
name: RDF Sync Published
description: 'Provides a way to filter out unpublished content from the RDF Sync module.'
type: module
core_version_requirement: ^10.2
dependencies:
- rdf_sync:rdf_sync
services:
rdf_sync_published.sync.subscriber:
class: Drupal\rdf_sync_published\EventSubscriber\RdfSyncPublishedSubscriber
tags:
- { name: event_subscriber }
<?php
declare(strict_types=1);
namespace Drupal\rdf_sync_published\EventSubscriber;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\rdf_sync\Event\RdfSyncEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listens to the event before syncing the entities.
*/
class RdfSyncPublishedSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
RdfSyncEvent::class => 'filterOutUnpublished',
];
}
/**
* Filters out unpublished entities.
*
* @param \Drupal\rdf_sync\Event\RdfSyncEvent $event
* The event object.
*/
public function filterOutUnpublished(RdfSyncEvent $event): void {
$entities = $event->getEntities();
// Do not filter out entities that do not implement
// EntityPublishedInterface as we cannot determine their publication
// status and might filter out entities that should be synced.
$entities = array_filter($entities, fn ($entity): bool => !$entity instanceof EntityPublishedInterface || $entity->isPublished());
$event->setEntities($entities);
}
}
<?php
declare(strict_types=1);
namespace Drupal\Tests\rdf_sync_published\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\rdf_sync\Traits\RdfSyncTestTrait;
use Drupal\rdf_sync\Model\SyncMethod;
/**
* Tests the RDF sync by publication status.
*
* @group rdf_sync
*/
class RdfSyncPublishedTest extends KernelTestBase {
use RdfSyncTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'field',
'filter',
'node',
'rdf_sync',
'rdf_sync_published',
'rdf_sync_test',
'serialization',
'system',
'taxonomy',
'text',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installSchema('node', ['node_access']);
$this->installConfig(['rdf_sync_test', 'rdf_sync']);
$this->installSchema('rdf_sync', ['rdf_sync_uri']);
$this->config('rdf_sync.settings')->set('endpoint.host', 'virtuoso')->save();
$this->setUpFields();
}
/**
* @covers \Drupal\rdf_sync_published\EventSubscriber\RdfSyncPublishedSubscriber
*/
public function testSynchronization(): void {
$node = $this->container->get('entity_type.manager')->getStorage('node')->create([
'type' => 'page',
'title' => 'Some title',
// Initially create the node as unpublished.
'status' => 0,
'uri' => 'http://example.com/page/id/1',
]);
$node->save();
/** @var \Drupal\rdf_sync\RdfSyncSynchronizer $synchronizer */
$synchronizer = $this->container->get('rdf_sync.synchronizer');
$synchronizer->synchronize(SyncMethod::INSERT, [$node], TRUE);
$this->assertNoTriples('http://data', [
'<http://example.com/page/id/1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/page>',
'<http://example.com/page/id/1> <http://example.com/page/title> "Some title"@en',
]);
// Publish the entity and synchronize again.
$node->setPublished()->save();
$synchronizer->synchronize(SyncMethod::INSERT, [$node], TRUE);
$this->assertTriples('http://data', [
'<http://example.com/page/id/1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/page>',
'<http://example.com/page/id/1> <http://example.com/page/title> "Some title"@en',
]);
}
}
...@@ -54,6 +54,7 @@ services: ...@@ -54,6 +54,7 @@ services:
- '@rdf_sync.mapper' - '@rdf_sync.mapper'
- '@rdf_sync.normalizer' - '@rdf_sync.normalizer'
- '@rdf_sync.triples' - '@rdf_sync.triples'
- '@event_dispatcher'
tags: tags:
- { name: needs_destruction } - { name: needs_destruction }
......
<?php
declare(strict_types=1);
namespace Drupal\rdf_sync\Event;
use Drupal\rdf_sync\Model\SyncMethod;
use Symfony\Contracts\EventDispatcher\Event;
/**
* Occurs before syncing the entities to the endpoint.
*
* Subscribers are able to alter the list of entities to be synced or perform
* additional actions.
*/
class RdfSyncEvent extends Event {
public function __construct(
public readonly SyncMethod $syncMethod,
protected array $entities,
) {}
/**
* Returns the objects to be synced.
*
* @return \Drupal\Core\Entity\ContentEntityInterface[]
* The objects to be synced.
*/
public function getEntities(): array {
return $this->entities;
}
/**
* Sets the objects to be synced.
*
* @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
* The objects to be synced.
*
* @return $this
*/
public function setEntities(array $entities): self {
$this->entities = $entities;
return $this;
}
}
<?php <?php
declare(strict_types=1);
namespace Drupal\rdf_sync\Form; namespace Drupal\rdf_sync\Form;
use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\ConfigFormBase;
......
...@@ -129,7 +129,7 @@ class RdfSyncNormalizer extends EntityNormalizer { ...@@ -129,7 +129,7 @@ class RdfSyncNormalizer extends EntityNormalizer {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function supportsNormalization($data, string $format = NULL, array $context = []): bool { public function supportsNormalization($data, ?string $format = NULL, array $context = []): bool {
return $this->checkFormat($format) && $this->mapper->isMappedEntity(entity: $data); return $this->checkFormat($format) && $this->mapper->isMappedEntity(entity: $data);
} }
......
...@@ -8,7 +8,9 @@ use Drupal\Core\DestructableInterface; ...@@ -8,7 +8,9 @@ use Drupal\Core\DestructableInterface;
use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Queue\QueueFactory; use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
use Drupal\rdf_sync\Event\RdfSyncEvent;
use Drupal\rdf_sync\Model\SyncMethod; use Drupal\rdf_sync\Model\SyncMethod;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
...@@ -29,6 +31,7 @@ class RdfSyncSynchronizer implements DestructableInterface { ...@@ -29,6 +31,7 @@ class RdfSyncSynchronizer implements DestructableInterface {
protected RdfSyncMapperInterface $mapper, protected RdfSyncMapperInterface $mapper,
protected NormalizerInterface $normalizer, protected NormalizerInterface $normalizer,
protected RdfSyncTriplesInterface $rdfSyncTriples, protected RdfSyncTriplesInterface $rdfSyncTriples,
protected EventDispatcherInterface $eventDispatcher,
) {} ) {}
/** /**
...@@ -99,6 +102,10 @@ class RdfSyncSynchronizer implements DestructableInterface { ...@@ -99,6 +102,10 @@ class RdfSyncSynchronizer implements DestructableInterface {
* The entities for which to update the RDF backend. * The entities for which to update the RDF backend.
*/ */
protected function doSynchronize(SyncMethod $method, array $entities): void { protected function doSynchronize(SyncMethod $method, array $entities): void {
$event = new RdfSyncEvent($method, $entities);
$this->eventDispatcher->dispatch($event);
$entities = $event->getEntities();
if (in_array($method, [SyncMethod::UPDATE, SyncMethod::DELETE], TRUE)) { if (in_array($method, [SyncMethod::UPDATE, SyncMethod::DELETE], TRUE)) {
$uris = array_filter( $uris = array_filter(
array_map( array_map(
......
...@@ -4,8 +4,8 @@ declare(strict_types=1); ...@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Drupal\Tests\rdf_sync\Functional; namespace Drupal\Tests\rdf_sync\Functional;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
use Drupal\node\Entity\NodeType;
/** /**
* Test entity config form. * Test entity config form.
......
...@@ -4,8 +4,8 @@ declare(strict_types=1); ...@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Drupal\Tests\rdf_sync\Functional; namespace Drupal\Tests\rdf_sync\Functional;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
use Drupal\node\Entity\NodeType;
/** /**
* Test field config form. * Test field config form.
......
...@@ -48,11 +48,9 @@ class RdfSyncDrushTest extends BrowserTestBase { ...@@ -48,11 +48,9 @@ class RdfSyncDrushTest extends BrowserTestBase {
public function testCommands(): void { public function testCommands(): void {
$this->drush('config:set', ['rdf_sync.settings', 'endpoint.host', 'virtuoso']); $this->drush('config:set', ['rdf_sync.settings', 'endpoint.host', 'virtuoso']);
$this->drush('rdf_sync:enable'); $this->toggleSynchronizationInDrush('disable');
$this->assertFalse(\Drupal::keyValue('state')->has('rdf_sync.synchronization_disabled')); $this->toggleSynchronizationInDrush('enable');
$this->toggleSynchronizationInDrush('disable');
$this->drush('rdf_sync:disable');
$this->assertTrue(\Drupal::state()->get('rdf_sync.synchronization_disabled'));
[$term, $node] = $this->createEntities(); [$term, $node] = $this->createEntities();
...@@ -73,7 +71,7 @@ class RdfSyncDrushTest extends BrowserTestBase { ...@@ -73,7 +71,7 @@ class RdfSyncDrushTest extends BrowserTestBase {
'<http://example.com/page/id/1> <http://example.com/page/body> "Strong is stripped while <em>Emphasis</em> is kept"@en', '<http://example.com/page/id/1> <http://example.com/page/body> "Strong is stripped while <em>Emphasis</em> is kept"@en',
]); ]);
$this->drush('rdf_sync:enable'); $this->toggleSynchronizationInDrush('enable');
$node->delete(); $node->delete();
$term->delete(); $term->delete();
...@@ -91,4 +89,18 @@ class RdfSyncDrushTest extends BrowserTestBase { ...@@ -91,4 +89,18 @@ class RdfSyncDrushTest extends BrowserTestBase {
]); ]);
} }
/**
* Toggle synchronization in Drush.
*
* @param string $action
* Whether to enable or disable synchronization.
*/
protected function toggleSynchronizationInDrush(string $action): void {
$this->drush('rdf_sync:' . $action);
// Mimic the refresh of the request as the state is updated in a separate
// process and the state in the container is already loaded and cached.
$this->container->get('state')->resetCache();
$this->assertEquals($action === 'disable', \Drupal::state()->get('rdf_sync.synchronization_disabled'));
}
} }
<?php <?php
declare(strict_types=1);
namespace Drupal\geocoder\Tests; namespace Drupal\geocoder\Tests;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
......
...@@ -7,10 +7,10 @@ namespace Drupal\Tests\rdf_sync\Kernel; ...@@ -7,10 +7,10 @@ namespace Drupal\Tests\rdf_sync\Kernel;
use Drupal\Component\Serialization\Yaml; use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\rdf_sync\Traits\RdfSyncTestTrait;
use Drupal\rdf_sync\Model\SyncMethod; use Drupal\rdf_sync\Model\SyncMethod;
use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary; use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rdf_sync\Traits\RdfSyncTestTrait;
use PHPUnit\Framework\Assert; use PHPUnit\Framework\Assert;
/** /**
......
...@@ -4,11 +4,11 @@ declare(strict_types=1); ...@@ -4,11 +4,11 @@ declare(strict_types=1);
namespace Drupal\Tests\rdf_sync\Traits; namespace Drupal\Tests\rdf_sync\Traits;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\Node; use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use PHPUnit\Framework\Assert; use PHPUnit\Framework\Assert;
/** /**
...@@ -145,7 +145,7 @@ trait RdfSyncTestTrait { ...@@ -145,7 +145,7 @@ trait RdfSyncTestTrait {
$failures = []; $failures = [];
foreach ($triples as $triple) { foreach ($triples as $triple) {
$tripeExists = $connection->query("ASK WHERE { GRAPH <$graph> { $triple } }")->getBoolean(); $tripeExists = $connection->query("ASK WHERE { GRAPH <$graph> { $triple } }")->getBoolean();
if ($checkExistence && !$tripeExists) { if ($checkExistence xor $tripeExists) {
$failures[] = $triple; $failures[] = $triple;
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment