diff --git a/jsonld.api.php b/jsonld.api.php new file mode 100644 index 0000000..7066ac1 --- /dev/null +++ b/jsonld.api.php @@ -0,0 +1,32 @@ +getEntityTypeId() == 'node') { + if (isset($normalized['@graph'])) { + if (!is_array($normalized["@graph"])) { + $normalized['@graph'] = [$normalized['@graph']]; + } + $normalized['@graph'][] = [ + '@id' => 'http://example.org/first/name', + '@type' => 'schemaOrg:Person', + ]; + } + } +} diff --git a/jsonld.services.yml b/jsonld.services.yml index fb07de3..0cfaf15 100644 --- a/jsonld.services.yml +++ b/jsonld.services.yml @@ -17,10 +17,10 @@ services: class: Drupal\jsonld\Normalizer\FileEntityNormalizer tags: - { name: normalizer, priority: 20 } - arguments: ['@entity.manager', '@http_client', '@hal.link_manager', '@module_handler'] + arguments: ['@entity_type.manager', '@http_client', '@hal.link_manager', '@module_handler', '@file_system'] serializer.normalizer.entity.jsonld: class: Drupal\jsonld\Normalizer\ContentEntityNormalizer - arguments: ['@hal.link_manager', '@entity.manager', '@module_handler'] + arguments: ['@hal.link_manager', '@entity_type.manager', '@module_handler'] tags: - { name: normalizer, priority: 10 } serializer.encoder.jsonld: diff --git a/src/Normalizer/ContentEntityNormalizer.php b/src/Normalizer/ContentEntityNormalizer.php index 4b59567..99315f9 100644 --- a/src/Normalizer/ContentEntityNormalizer.php +++ b/src/Normalizer/ContentEntityNormalizer.php @@ -3,7 +3,7 @@ namespace Drupal\jsonld\Normalizer; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\hal\LinkManager\LinkManagerInterface; use Symfony\Component\Serializer\Exception\UnexpectedValueException; @@ -13,6 +13,8 @@ */ class ContentEntityNormalizer extends NormalizerBase { + const NORMALIZE_ALTER_HOOK = "jsonld_alter_normalized_array"; + /** * The interface or class that this Normalizer supports. * @@ -46,12 +48,12 @@ class ContentEntityNormalizer extends NormalizerBase { * * @param \Drupal\hal\LinkManager\LinkManagerInterface $link_manager * The hypermedia link manager. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager * The entity manager. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. */ - public function __construct(LinkManagerInterface $link_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) { + public function __construct(LinkManagerInterface $link_manager, EntityTypeManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) { $this->linkManager = $link_manager; $this->entityManager = $entity_manager; @@ -67,12 +69,17 @@ public function normalize($entity, $format = NULL, array $context = []) { // @TODO check $format before going RDF crazy $normalized = []; + if (isset($context['depth'])) { + $context['depth'] += 1; + } + $context += [ 'account' => NULL, 'included_fields' => NULL, 'needs_jsonldcontext' => FALSE, 'embedded' => FALSE, 'namespaces' => rdf_get_namespaces(), + 'depth' => 0, ]; if ($context['needs_jsonldcontext']) { @@ -95,7 +102,7 @@ public function normalize($entity, $format = NULL, array $context = []) { // not shortened ones. So we replace them in place. if ($context['needs_jsonldcontext'] === FALSE && is_array($types)) { for ($i = 0; $i < count($types); $i++) { - $types[$i] = $this->escapePrefix($types[$i], $context['namespaces']); + $types[$i] = ContentEntityNormalizer::escapePrefix($types[$i], $context['namespaces']); } } @@ -126,6 +133,7 @@ public function normalize($entity, $format = NULL, array $context = []) { $context['current_entity_id'] = $this->getEntityUri($entity); $context['current_entity_rdf_mapping'] = $rdf_mappings; + foreach ($fields as $name => $field) { // Just process fields that have rdf mappings defined. // We could also pass as not contextualized keys the others @@ -150,6 +158,12 @@ public function normalize($entity, $format = NULL, array $context = []) { if (!$context['embedded']) { $normalized['@graph'] = array_values($normalized['@graph']); } + + if (isset($context['depth']) && $context['depth'] == 0) { + $this->moduleHandler->invokeAll(self::NORMALIZE_ALTER_HOOK, + [$entity, &$normalized, $context] + ); + } return $normalized; } @@ -238,9 +252,9 @@ protected function getEntityUri(EntityInterface $entity) { // Some entity types don't provide a canonical link template, at least call // out to ->url(). if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) { - return $entity->url('canonical', []); + return $entity->toUrl('canonical', []); } - $url = $entity->urlInfo('canonical', ['absolute' => TRUE]); + $url = $entity->toUrl('canonical', ['absolute' => TRUE]); return $url->setRouteParameter('_format', 'jsonld')->toString(); } diff --git a/src/Normalizer/FileEntityNormalizer.php b/src/Normalizer/FileEntityNormalizer.php index 6a3e9a8..0fe5198 100644 --- a/src/Normalizer/FileEntityNormalizer.php +++ b/src/Normalizer/FileEntityNormalizer.php @@ -2,8 +2,9 @@ namespace Drupal\jsonld\Normalizer; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\hal\LinkManager\LinkManagerInterface; use GuzzleHttp\ClientInterface; @@ -26,10 +27,17 @@ class FileEntityNormalizer extends ContentEntityNormalizer { */ protected $httpClient; + /** + * The Drupal file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a FileEntityNormalizer object. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager * The entity manager. * @param \GuzzleHttp\ClientInterface $http_client * The HTTP Client. @@ -37,12 +45,19 @@ class FileEntityNormalizer extends ContentEntityNormalizer { * The hypermedia link manager. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system handler. */ - public function __construct(EntityManagerInterface $entity_manager, ClientInterface $http_client, LinkManagerInterface $link_manager, ModuleHandlerInterface $module_handler) { + public function __construct(EntityTypeManagerInterface $entity_manager, + ClientInterface $http_client, + LinkManagerInterface $link_manager, + ModuleHandlerInterface $module_handler, + FileSystemInterface $file_system) { parent::__construct($link_manager, $entity_manager, $module_handler); $this->httpClient = $http_client; + $this->fileSystem = $file_system; } /** @@ -64,7 +79,7 @@ public function denormalize($data, $class, $format = NULL, array $context = []) $file_data = (string) $this->httpClient->get($data['uri'][0]['value'])->getBody(); - $path = 'temporary://' . drupal_basename($data['uri'][0]['value']); + $path = 'temporary://' . $this->fileSystem->basename($data['uri'][0]['value']); $data['uri'] = file_unmanaged_save_data($file_data, $path); return $this->entityManager->getStorage('file')->create($data); diff --git a/src/Normalizer/NormalizerBase.php b/src/Normalizer/NormalizerBase.php index a3e1468..cb66c76 100644 --- a/src/Normalizer/NormalizerBase.php +++ b/src/Normalizer/NormalizerBase.php @@ -55,7 +55,7 @@ public function supportsDenormalization($data, $type, $format = NULL) { * @return string * The predicate with escaped namespace prefix. */ - protected function escapePrefix($predicate, array $namespaces) { + public static function escapePrefix($predicate, array $namespaces) { $exploded = explode(":", $predicate); if (!isset($namespaces[$exploded[0]])) { diff --git a/tests/json_alter_normalize_hooks.info.yml b/tests/json_alter_normalize_hooks.info.yml new file mode 100644 index 0000000..105d1c9 --- /dev/null +++ b/tests/json_alter_normalize_hooks.info.yml @@ -0,0 +1,6 @@ +name: 'Jsonld hook tests' +type: module +package: Testing +core: 8.x +dependencies: + - jsonld diff --git a/tests/json_alter_normalize_hooks.module b/tests/json_alter_normalize_hooks.module new file mode 100644 index 0000000..8509a70 --- /dev/null +++ b/tests/json_alter_normalize_hooks.module @@ -0,0 +1,25 @@ + 'json_alter_normalize_hooks', + 'http://purl.org/dc/elements/1.1/title' => 'The hook is tested.', + ]; + } +} diff --git a/tests/src/Kernel/JsonldContextGeneratorTest.php b/tests/src/Kernel/JsonldContextGeneratorTest.php index 881584b..bc0a861 100644 --- a/tests/src/Kernel/JsonldContextGeneratorTest.php +++ b/tests/src/Kernel/JsonldContextGeneratorTest.php @@ -24,7 +24,6 @@ class JsonldContextGeneratorTest extends KernelTestBase { 'rdf_test_namespaces', 'serialization', 'system', - 'typed_data', ]; diff --git a/tests/src/Kernel/JsonldHookTest.php b/tests/src/Kernel/JsonldHookTest.php new file mode 100644 index 0000000..e98eb54 --- /dev/null +++ b/tests/src/Kernel/JsonldHookTest.php @@ -0,0 +1,49 @@ +rebuild(); + } + + /** + * Test hook alter. + */ + public function testAlterNormalizedJsonld() { + + list($entity, $expected) = $this->generateTestEntity(); + $expected['@graph'][] = [ + "@id" => "json_alter_normalize_hooks", + "http://purl.org/dc/elements/1.1/title" => "The hook is tested.", + ]; + + $normalized = $this->serializer->normalize($entity, $this->format); + $this->assertEquals($expected, $normalized, "Did not normalize and call hooks correctly."); + + } + +} diff --git a/tests/src/Kernel/JsonldKernelTestBase.php b/tests/src/Kernel/JsonldKernelTestBase.php index 4f3d5bd..e4cadb3 100644 --- a/tests/src/Kernel/JsonldKernelTestBase.php +++ b/tests/src/Kernel/JsonldKernelTestBase.php @@ -4,6 +4,7 @@ use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Entity\EntityInterface; +use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\jsonld\Encoder\JsonldEncoder; @@ -18,6 +19,7 @@ use Drupal\serialization\EntityResolver\ChainEntityResolver; use Drupal\serialization\EntityResolver\TargetIdResolver; use Drupal\serialization\EntityResolver\UuidResolver; +use Drupal\user\Entity\User; use Symfony\Component\Serializer\Serializer; /** @@ -181,10 +183,122 @@ protected function getEntityUri(EntityInterface $entity) { // Some entity types don't provide a canonical link template, at least call // out to ->url(). if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) { - return $entity->url('canonical', []); + return $entity->toUrl('canonical', []); } - $url = $entity->urlInfo('canonical', ['absolute' => TRUE]); + $url = $entity->toUrl('canonical', ['absolute' => TRUE]); return $url->setRouteParameter('_format', 'jsonld')->toString(); } + /** + * Generate a test entity and the expected normalized array. + * + * @return array + * with [ the entity, the normalized array ]. + */ + protected function generateTestEntity() { + $target_entity = EntityTest::create([ + 'name' => $this->randomMachineName(), + 'langcode' => 'en', + 'field_test_entity_reference' => NULL, + ]); + $target_entity->save(); + + $target_user = User::create([ + 'name' => $this->randomMachineName(), + 'langcode' => 'en', + ]); + $target_user->save(); + + rdf_get_mapping('entity_test', 'entity_test')->setBundleMapping( + [ + 'types' => [ + "schema:ImageObject", + ], + ])->setFieldMapping('field_test_text', [ + 'properties' => ['dc:description'], + ])->setFieldMapping('user_id', [ + 'properties' => ['schema:author'], + ])->setFieldMapping('modified', [ + 'properties' => ['schema:dateModified'], + 'datatype' => 'xsd:dateTime', + ])->save(); + + $tz = new \DateTimeZone('UTC'); + $dt = new \DateTime(NULL, $tz); + $created = $dt->format("U"); + $created_iso = $dt->format(\DateTime::W3C); + // Create an entity. + $values = [ + 'langcode' => 'en', + 'name' => $this->randomMachineName(), + 'type' => 'entity_test', + 'bundle' => 'entity_test', + 'user_id' => $target_user->id(), + 'created' => [ + 'value' => $created, + ], + 'field_test_text' => [ + 'value' => $this->randomMachineName(), + 'format' => 'full_html', + ], + 'field_test_entity_reference' => [ + 'target_id' => $target_entity->id(), + ], + ]; + + $entity = EntityTest::create($values); + $entity->save(); + + $expected = [ + "@graph" => [ + [ + "@id" => $this->getEntityUri($entity), + "@type" => [ + 'http://schema.org/ImageObject', + ], + "http://purl.org/dc/terms/references" => [ + [ + "@id" => $this->getEntityUri($target_entity), + ], + ], + "http://purl.org/dc/terms/description" => [ + [ + "@type" => "http://www.w3.org/2001/XMLSchema#string", + "@value" => $values['field_test_text']['value'], + ], + ], + "http://purl.org/dc/terms/title" => [ + [ + "@type" => "http://www.w3.org/2001/XMLSchema#string", + "@value" => $values['name'], + ], + ], + "http://schema.org/author" => [ + [ + "@id" => $this->getEntityUri($target_user), + ], + ], + "http://schema.org/dateCreated" => [ + [ + "@type" => "http://www.w3.org/2001/XMLSchema#dateTime", + "@value" => $created_iso, + ], + ], + ], + [ + "@id" => $this->getEntityUri($target_user), + "@type" => "http://localhost/rest/type/user/user", + ], + [ + "@id" => $this->getEntityUri($target_entity), + "@type" => [ + "http://schema.org/ImageObject", + ], + ], + ], + ]; + + return [$entity, $expected]; + } + } diff --git a/tests/src/Kernel/Normalizer/ContentEntityNormalizerTests.php b/tests/src/Kernel/Normalizer/ContentEntityNormalizerTests.php index 810b2e3..15908ec 100644 --- a/tests/src/Kernel/Normalizer/ContentEntityNormalizerTests.php +++ b/tests/src/Kernel/Normalizer/ContentEntityNormalizerTests.php @@ -2,9 +2,7 @@ namespace Drupal\Tests\jsonld\Kernel\Normalizer; -use Drupal\entity_test\Entity\EntityTest; use Drupal\Tests\jsonld\Kernel\JsonldKernelTestBase; -use Drupal\user\Entity\User; /** * Class ContentEntityTests. @@ -34,107 +32,7 @@ protected function setUp() { */ public function testSimpleNormalizeJsonld() { - $target_entity = EntityTest::create([ - 'name' => $this->randomMachineName(), - 'langcode' => 'en', - 'field_test_entity_reference' => NULL, - ]); - $target_entity->save(); - - $target_user = User::create([ - 'name' => $this->randomMachineName(), - 'langcode' => 'en', - ]); - $target_user->save(); - - rdf_get_mapping('entity_test', 'entity_test')->setBundleMapping( - [ - 'types' => [ - "schema:ImageObject", - ], - ])->setFieldMapping('field_test_text', [ - 'properties' => ['dc:description'], - ])->setFieldMapping('user_id', [ - 'properties' => ['schema:author'], - ])->setFieldMapping('modified', [ - 'properties' => ['schema:dateModified'], - 'datatype' => 'xsd:dateTime', - ])->save(); - - $tz = new \DateTimeZone('UTC'); - $dt = new \DateTime(NULL, $tz); - $created = $dt->format("U"); - $created_iso = $dt->format(\DateTime::W3C); - // Create an entity. - $values = [ - 'langcode' => 'en', - 'name' => $this->randomMachineName(), - 'type' => 'entity_test', - 'bundle' => 'entity_test', - 'user_id' => $target_user->id(), - 'created' => [ - 'value' => $created, - ], - 'field_test_text' => [ - 'value' => $this->randomMachineName(), - 'format' => 'full_html', - ], - 'field_test_entity_reference' => [ - 'target_id' => $target_entity->id(), - ], - ]; - - $entity = EntityTest::create($values); - $entity->save(); - - $expected = [ - "@graph" => [ - [ - "@id" => $this->getEntityUri($entity), - "@type" => [ - 'http://schema.org/ImageObject', - ], - "http://purl.org/dc/terms/references" => [ - [ - "@id" => $this->getEntityUri($target_entity), - ], - ], - "http://purl.org/dc/terms/description" => [ - [ - "@type" => "http://www.w3.org/2001/XMLSchema#string", - "@value" => $values['field_test_text']['value'], - ], - ], - "http://purl.org/dc/terms/title" => [ - [ - "@type" => "http://www.w3.org/2001/XMLSchema#string", - "@value" => $values['name'], - ], - ], - "http://schema.org/author" => [ - [ - "@id" => $this->getEntityUri($target_user), - ], - ], - "http://schema.org/dateCreated" => [ - [ - "@type" => "http://www.w3.org/2001/XMLSchema#dateTime", - "@value" => $created_iso, - ], - ], - ], - [ - "@id" => $this->getEntityUri($target_user), - "@type" => "http://localhost/rest/type/user/user", - ], - [ - "@id" => $this->getEntityUri($target_entity), - "@type" => [ - "http://schema.org/ImageObject", - ], - ], - ], - ]; + list($entity, $expected) = $this->generateTestEntity(); $normalized = $this->serializer->normalize($entity, $this->format); $this->assertEquals($expected, $normalized, "Did not normalize correctly.");