From 20add3818724237ce5263fc6cce02d46b101a8e3 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Fri, 8 May 2015 15:58:10 -0700 Subject: [PATCH] Issue #2336247 by larowlan, andythorne: Make Relation and Type domain configurable based on context --- .../Normalizer/ContentEntityNormalizer.php | 11 +-- .../EntityReferenceItemNormalizer.php | 2 +- .../hal/src/Tests/FileNormalizeTest.php | 3 +- .../hal/src/Tests/NormalizerTestBase.php | 4 +- .../rest/config/install/rest.settings.yml | 4 + .../rest/config/schema/rest.schema.yml | 3 + core/modules/rest/rest.api.php | 47 ++++++++++ core/modules/rest/rest.services.yml | 4 +- .../ConfigurableLinkManagerInterface.php | 25 ++++++ .../rest/src/LinkManager/LinkManager.php | 21 +++-- .../rest/src/LinkManager/LinkManagerBase.php | 64 +++++++++++++ .../src/LinkManager/RelationLinkManager.php | 50 +++++++---- .../RelationLinkManagerInterface.php | 6 +- .../rest/src/LinkManager/TypeLinkManager.php | 56 ++++++++---- .../LinkManager/TypeLinkManagerInterface.php | 10 ++- .../rest/src/Tests/RestLinkManagerTest.php | 90 +++++++++++++++++++ .../modules/rest_test/rest_test.info.yml | 8 ++ .../tests/modules/rest_test/rest_test.module | 24 +++++ .../src/Tests/EntityResolverTest.php | 1 - .../src/Tests/NormalizerTestBase.php | 4 +- 20 files changed, 376 insertions(+), 61 deletions(-) create mode 100644 core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php create mode 100644 core/modules/rest/src/LinkManager/LinkManagerBase.php create mode 100644 core/modules/rest/src/Tests/RestLinkManagerTest.php create mode 100644 core/modules/rest/tests/modules/rest_test/rest_test.info.yml create mode 100644 core/modules/rest/tests/modules/rest_test/rest_test.module diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index a3caa35bfcb..14fdb0b4406 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -76,7 +76,7 @@ class ContentEntityNormalizer extends NormalizerBase { 'href' => $this->getEntityUri($entity), ), 'type' => array( - 'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle()), + 'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle(), $context), ), ), ); @@ -132,7 +132,7 @@ class ContentEntityNormalizer extends NormalizerBase { } // Create the entity. - $typed_data_ids = $this->getTypedDataIds($data['_links']['type']); + $typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context); $entity_type = $this->entityManager->getDefinition($typed_data_ids['entity_type']); $langcode_key = $entity_type->getKey('langcode'); $values = array(); @@ -210,13 +210,14 @@ class ContentEntityNormalizer extends NormalizerBase { * * @param array $types * The type array(s) (value of the 'type' attribute of the incoming data). + * @param array $context + * Context from the normalizer/serializer operation. * * @return array * The typed data IDs. * - * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException */ - protected function getTypedDataIds($types) { + protected function getTypedDataIds($types, $context = array()) { // The 'type' can potentially contain an array of type objects. By default, // Drupal only uses a single type in serializing, but allows for multiple // types when deserializing. @@ -231,7 +232,7 @@ class ContentEntityNormalizer extends NormalizerBase { $type_uri = $type['href']; // Check whether the URI corresponds to a known type on this site. Break // once one does. - if ($typed_data_ids = $this->linkManager->getTypeInternalIds($type['href'])) { + if ($typed_data_ids = $this->linkManager->getTypeInternalIds($type['href'], $context)) { break; } } diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index 4b5344a6e54..5160b68d012 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -85,7 +85,7 @@ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidR // objects. $field_name = $field_item->getParent()->getName(); $entity = $field_item->getEntity(); - $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name); + $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name, $context); return array( '_links' => array( $field_uri => array($link), diff --git a/core/modules/hal/src/Tests/FileNormalizeTest.php b/core/modules/hal/src/Tests/FileNormalizeTest.php index 2b5038166d8..64ffd0797b3 100644 --- a/core/modules/hal/src/Tests/FileNormalizeTest.php +++ b/core/modules/hal/src/Tests/FileNormalizeTest.php @@ -39,8 +39,7 @@ class FileNormalizeTest extends NormalizerTestBase { $this->installEntitySchema('file'); $entity_manager = \Drupal::entityManager(); - $url_assembler = \Drupal::service('unrouted_url_assembler'); - $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), $url_assembler), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, $url_assembler)); + $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack')), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack'))); // Set up the mock serializer. $normalizers = array( diff --git a/core/modules/hal/src/Tests/NormalizerTestBase.php b/core/modules/hal/src/Tests/NormalizerTestBase.php index 43f10b1295e..aea5b2525e4 100644 --- a/core/modules/hal/src/Tests/NormalizerTestBase.php +++ b/core/modules/hal/src/Tests/NormalizerTestBase.php @@ -83,6 +83,7 @@ abstract class NormalizerTestBase extends KernelTestBase { $class = get_parent_class($class); } $this->installConfig(array('field', 'language')); + \Drupal::service('router.builder')->rebuild(); // Add German as a language. ConfigurableLanguage::create(array( @@ -134,8 +135,7 @@ abstract class NormalizerTestBase extends KernelTestBase { ))->save(); $entity_manager = \Drupal::entityManager(); - $url_assembler = \Drupal::service('unrouted_url_assembler'); - $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), $url_assembler), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, $url_assembler)); + $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack')), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack'))); $chain_resolver = new ChainEntityResolver(array(new UuidResolver($entity_manager), new TargetIdResolver())); diff --git a/core/modules/rest/config/install/rest.settings.yml b/core/modules/rest/config/install/rest.settings.yml index 0a4434677f6..0c388cad2f6 100644 --- a/core/modules/rest/config/install/rest.settings.yml +++ b/core/modules/rest/config/install/rest.settings.yml @@ -44,3 +44,7 @@ resources: # # The full documentation is located at # https://drupal.org/documentation/modules/rest + +# Set the domain for REST type and relation links. +# If left blank, the site's domain will be used +link_domain: ~ diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml index 8421d13e896..8014b31f566 100644 --- a/core/modules/rest/config/schema/rest.schema.yml +++ b/core/modules/rest/config/schema/rest.schema.yml @@ -10,6 +10,9 @@ rest.settings: sequence: type: rest_resource label: 'Resource' + link_domain: + type: string + label: 'Domain of the relation' rest_resource: type: mapping diff --git a/core/modules/rest/rest.api.php b/core/modules/rest/rest.api.php index 2f3f6ee2c0d..fb8993d2680 100644 --- a/core/modules/rest/rest.api.php +++ b/core/modules/rest/rest.api.php @@ -28,6 +28,53 @@ function hook_rest_resource_alter(&$definitions) { unset($definitions['entity:view']); } +/** + * Alter the REST type URI. + * + * Modules may wish to alter the type URI generated for a resource based on the + * context of the serializer/normalizer operation. + * + * @param string $uri + * The URI to alter. + * @param array $context + * The context from the serializer/normalizer operation. + * + * @see \Symfony\Component\Serializer\SerializerInterface::serialize() + * @see \Symfony\Component\Serializer\SerializerInterface::deserialize() + * @see \Symfony\Component\Serializer\NormalizerInterface::normalize() + * @see \Symfony\Component\Serializer\DenormalizerInterface::denormalize() + */ +function hook_rest_type_uri_alter(&$uri, $context = array()) { + if ($context['mymodule'] == TRUE) { + $base = \Drupal::config('rest.settings')->get('link_domain'); + $uri = str_replace($base, 'http://mymodule.domain', $uri); + } +} + + +/** + * Alter the REST relation URI. + * + * Modules may wish to alter the relation URI generated for a resource based on + * the context of the serializer/normalizer operation. + * + * @param string $uri + * The URI to alter. + * @param array $context + * The context from the serializer/normalizer operation. + * + * @see \Symfony\Component\Serializer\SerializerInterface::serialize() + * @see \Symfony\Component\Serializer\SerializerInterface::deserialize() + * @see \Symfony\Component\Serializer\NormalizerInterface::normalize() + * @see \Symfony\Component\Serializer\DenormalizerInterface::denormalize() + */ +function hook_rest_relation_uri_alter(&$uri, $context = array()) { + if ($context['mymodule'] == TRUE) { + $base = \Drupal::config('rest.settings')->get('link_domain'); + $uri = str_replace($base, 'http://mymodule.domain', $uri); + } +} + /** * @} End of "addtogroup hooks". */ diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index bfba7bb5688..04f5f1d5828 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -19,10 +19,10 @@ services: arguments: ['@rest.link_manager.type', '@rest.link_manager.relation'] rest.link_manager.type: class: Drupal\rest\LinkManager\TypeLinkManager - arguments: ['@cache.default', '@unrouted_url_assembler'] + arguments: ['@cache.default', '@module_handler', '@config.factory', '@request_stack'] rest.link_manager.relation: class: Drupal\rest\LinkManager\RelationLinkManager - arguments: ['@cache.default', '@entity.manager', '@unrouted_url_assembler'] + arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory', '@request_stack'] rest.resource_routes: class: Drupal\rest\Routing\ResourceRoutes arguments: ['@plugin.manager.rest', '@config.factory', '@logger.channel.rest'] diff --git a/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php b/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php new file mode 100644 index 00000000000..0505811d8b0 --- /dev/null +++ b/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php @@ -0,0 +1,25 @@ +typeLinkManager->getTypeUri($entity_type, $bundle); + public function getTypeUri($entity_type, $bundle, $context = array()) { + return $this->typeLinkManager->getTypeUri($entity_type, $bundle, $context); } /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeInternalIds(). */ - public function getTypeInternalIds($type_uri) { - return $this->typeLinkManager->getTypeInternalIds($type_uri); + public function getTypeInternalIds($type_uri, $context = array()) { + return $this->typeLinkManager->getTypeInternalIds($type_uri, $context); } /** * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationUri(). */ - public function getRelationUri($entity_type, $bundle, $field_name) { - return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name); + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { + return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name, $context); } /** @@ -63,4 +63,13 @@ class LinkManager implements LinkManagerInterface { return $this->relationLinkManager->getRelationInternalIds($relation_uri); } + /** + * {@inheritdoc} + */ + public function setLinkDomain($domain) { + $this->relationLinkManager->setLinkDomain($domain); + $this->typeLinkManager->setLinkDomain($domain); + return $this; + } + } diff --git a/core/modules/rest/src/LinkManager/LinkManagerBase.php b/core/modules/rest/src/LinkManager/LinkManagerBase.php new file mode 100644 index 00000000000..f93f6fdd567 --- /dev/null +++ b/core/modules/rest/src/LinkManager/LinkManagerBase.php @@ -0,0 +1,64 @@ +linkDomain = rtrim($domain, '/'); + return $this; + } + + /** + * Gets the link domain. + * + * @return string + * The link domain. + */ + protected function getLinkDomain() { + if (empty($this->linkDomain)) { + if ($domain = $this->configFactory->get('rest.settings')->get('link_domain')) { + $this->linkDomain = rtrim($domain, '/'); + } + else { + $request = $this->requestStack->getCurrentRequest(); + $this->linkDomain = $request->getSchemeAndHttpHost() . $request->getBasePath(); + } + } + return $this->linkDomain; + } + +} diff --git a/core/modules/rest/src/LinkManager/RelationLinkManager.php b/core/modules/rest/src/LinkManager/RelationLinkManager.php index 38bf4cfcc85..9cdd5c48f1e 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManager.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManager.php @@ -9,11 +9,13 @@ namespace Drupal\rest\LinkManager; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\HttpFoundation\RequestStack; -class RelationLinkManager implements RelationLinkManagerInterface { +class RelationLinkManager extends LinkManagerBase implements RelationLinkManagerInterface { /** * @var \Drupal\Core\Cache\CacheBackendInterface; @@ -28,11 +30,11 @@ class RelationLinkManager implements RelationLinkManagerInterface { protected $entityManager; /** - * The unrouted URL assembler. + * Module handler service. * - * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface + * @var \Drupal\Core\Extension\ModuleHandlerInterface */ - protected $urlAssembler; + protected $moduleHandler; /** * Constructor. @@ -41,27 +43,35 @@ class RelationLinkManager implements RelationLinkManagerInterface { * The cache of relation URIs and their associated Typed Data IDs. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface $url_assembler - * The unrouted URL assembler. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory service. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, UnroutedUrlAssemblerInterface $url_assembler) { + public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { $this->cache = $cache; $this->entityManager = $entity_manager; - $this->urlAssembler = $url_assembler; + $this->configFactory = $config_factory; + $this->moduleHandler = $module_handler; + $this->requestStack = $request_stack; } /** * {@inheritdoc} */ - public function getRelationUri($entity_type, $bundle, $field_name) { - return $this->urlAssembler->assemble("base:rest/relation/$entity_type/$bundle/$field_name", array('absolute' => TRUE)); + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { + $uri = $this->getLinkDomain() . "/rest/relation/$entity_type/$bundle/$field_name"; + $this->moduleHandler->alter('rest_relation_uri', $uri, $context); + return $uri; } /** * {@inheritdoc} */ - public function getRelationInternalIds($relation_uri) { - $relations = $this->getRelations(); + public function getRelationInternalIds($relation_uri, $context = array()) { + $relations = $this->getRelations($context); if (isset($relations[$relation_uri])) { return $relations[$relation_uri]; } @@ -77,15 +87,18 @@ class RelationLinkManager implements RelationLinkManagerInterface { * even primitives, are given a relation URI. It is up to the caller to * determine which URIs to use. * + * @param array $context + * Context from the normalizer/serializer operation. + * * @return array * An array of typed data ids (entity_type, bundle, and field name) keyed * by corresponding relation URI. */ - protected function getRelations() { + protected function getRelations($context = array()) { $cid = 'rest:links:relations'; $cache = $this->cache->get($cid); if (!$cache) { - $this->writeCache(); + $this->writeCache($context); $cache = $this->cache->get($cid); } return $cache->data; @@ -93,15 +106,18 @@ class RelationLinkManager implements RelationLinkManagerInterface { /** * Writes the cache of relation links. + * + * @param array $context + * Context from the normalizer/serializer operation. */ - protected function writeCache() { + protected function writeCache($context = array()) { $data = array(); foreach ($this->entityManager->getDefinitions() as $entity_type) { if ($entity_type instanceof ContentEntityTypeInterface) { foreach ($this->entityManager->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) { foreach ($this->entityManager->getFieldDefinitions($entity_type->id(), $bundle) as $field_definition) { - $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName()); + $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName(), $context); $data[$relation_uri] = array( 'entity_type' => $entity_type, 'bundle' => $bundle, diff --git a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php index 2dc175caaaf..f99e43e05b9 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php @@ -7,7 +7,7 @@ namespace Drupal\rest\LinkManager; -interface RelationLinkManagerInterface { +interface RelationLinkManagerInterface extends ConfigurableLinkManagerInterface { /** * Gets the URI that corresponds to a field. @@ -18,11 +18,13 @@ interface RelationLinkManagerInterface { * The bundle name. * @param string $field_name * The field name. + * @param array $context + * (optional) Optional serializer/normalizer context. * * @return string * The corresponding URI for the field. */ - public function getRelationUri($entity_type, $bundle, $field_name); + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()); /** * Translates a REST URI into internal IDs. diff --git a/core/modules/rest/src/LinkManager/TypeLinkManager.php b/core/modules/rest/src/LinkManager/TypeLinkManager.php index 4160b2b4d78..3c9f66511b2 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManager.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManager.php @@ -9,9 +9,11 @@ namespace Drupal\rest\LinkManager; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\HttpFoundation\RequestStack; -class TypeLinkManager implements TypeLinkManagerInterface { +class TypeLinkManager extends LinkManagerBase implements TypeLinkManagerInterface { /** * Injected cache backend. @@ -21,23 +23,29 @@ class TypeLinkManager implements TypeLinkManagerInterface { protected $cache; /** - * The unrouted URL assembler. + * Module handler service. * - * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface + * @var \Drupal\Core\Extension\ModuleHandlerInterface */ - protected $urlAssembler; + protected $moduleHandler; /** * Constructor. * * @param \Drupal\Core\Cache\CacheBackendInterface $cache * The injected cache backend for caching type URIs. - * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface $url_assembler - * The unrouted URL assembler. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory service. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(CacheBackendInterface $cache, UnroutedUrlAssemblerInterface $url_assembler) { + public function __construct(CacheBackendInterface $cache, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { $this->cache = $cache; - $this->urlAssembler = $url_assembler; + $this->configFactory = $config_factory; + $this->moduleHandler = $module_handler; + $this->requestStack = $request_stack; } /** @@ -47,20 +55,23 @@ class TypeLinkManager implements TypeLinkManagerInterface { * The bundle's entity type. * @param string $bundle * The name of the bundle. + * @param array $context + * Context of normalizer/serializer. * - * @return array + * @return string * The URI that identifies this bundle. */ - public function getTypeUri($entity_type, $bundle) { - // @todo Make the base path configurable. - return $this->urlAssembler->assemble("base:rest/type/$entity_type/$bundle", array('absolute' => TRUE)); + public function getTypeUri($entity_type, $bundle, $context = array()) { + $uri = $this->getLinkDomain() . "/rest/type/$entity_type/$bundle"; + $this->moduleHandler->alter('rest_type_uri', $uri, $context); + return $uri; } /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeInternalIds(). */ - public function getTypeInternalIds($type_uri) { - $types = $this->getTypes(); + public function getTypeInternalIds($type_uri, $context = array()) { + $types = $this->getTypes($context); if (isset($types[$type_uri])) { return $types[$type_uri]; } @@ -70,15 +81,18 @@ class TypeLinkManager implements TypeLinkManagerInterface { /** * Get the array of type links. * + * @param array $context + * Context from the normalizer/serializer operation. + * * @return array * An array of typed data ids (entity_type and bundle) keyed by * corresponding type URI. */ - protected function getTypes() { + protected function getTypes($context = array()) { $cid = 'rest:links:types'; $cache = $this->cache->get($cid); if (!$cache) { - $this->writeCache(); + $this->writeCache($context); $cache = $this->cache->get($cid); } return $cache->data; @@ -86,8 +100,11 @@ class TypeLinkManager implements TypeLinkManagerInterface { /** * Writes the cache of type links. + * + * @param array $context + * Context from the normalizer/serializer operation. */ - protected function writeCache() { + protected function writeCache($context = array()) { $data = array(); // Type URIs correspond to bundles. Iterate through the bundles to get the @@ -101,7 +118,7 @@ class TypeLinkManager implements TypeLinkManagerInterface { } foreach ($bundles as $bundle => $bundle_info) { // Get a type URI for the bundle. - $bundle_uri = $this->getTypeUri($entity_type_id, $bundle); + $bundle_uri = $this->getTypeUri($entity_type_id, $bundle, $context); $data[$bundle_uri] = array( 'entity_type' => $entity_type_id, 'bundle' => $bundle, @@ -112,4 +129,5 @@ class TypeLinkManager implements TypeLinkManagerInterface { // and only clear it when entity_info is cleared. $this->cache->set('rest:links:types', $data, Cache::PERMANENT, array('entity_types')); } + } diff --git a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php index 9c2d942fccf..3459d06c5fd 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php @@ -7,7 +7,7 @@ namespace Drupal\rest\LinkManager; -interface TypeLinkManagerInterface { +interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface { /** * Gets the URI that corresponds to a bundle. @@ -20,21 +20,25 @@ interface TypeLinkManagerInterface { * The bundle's entity type. * @param $bundle * The bundle name. + * @param array $context + * (optional) Optional serializer/normalizer context. * * @return string * The corresponding URI for the bundle. */ - public function getTypeUri($entity_type, $bundle); + public function getTypeUri($entity_type, $bundle, $context = array()); /** * Get a bundle's Typed Data IDs based on a URI. * * @param string $type_uri * The type URI. + * @param array $context + * Context from the normalizer/serializer operation. * * @return array | boolean * If the URI matches a bundle, returns an array containing entity_type and * bundle. Otherwise, returns false. */ - public function getTypeInternalIds($type_uri); + public function getTypeInternalIds($type_uri, $context = array()); } diff --git a/core/modules/rest/src/Tests/RestLinkManagerTest.php b/core/modules/rest/src/Tests/RestLinkManagerTest.php new file mode 100644 index 00000000000..4adee7c3246 --- /dev/null +++ b/core/modules/rest/src/Tests/RestLinkManagerTest.php @@ -0,0 +1,90 @@ +installSchema('system', ['router']); + \Drupal::service('router.builder')->rebuild(); + } + + /** + * Tests that type hooks work as expected. + */ + public function testRestLinkManagers() { + \Drupal::moduleHandler()->invoke('rest', 'install'); + /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ + $type_manager = \Drupal::service('rest.link_manager.type'); + $base = Url::fromRoute('', [], ['absolute' => TRUE])->toString(); + $link = $type_manager->getTypeUri('node', 'page'); + $this->assertEqual($link, $base . 'rest/type/node/page'); + // Now with optional context. + $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); + $this->assertEqual($link, 'rest_test_type'); + + /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ + $relation_manager = \Drupal::service('rest.link_manager.relation'); + $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); + $this->assertEqual($link, $base . 'rest/relation/node/page/field_ref'); + // Now with optional context. + $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); + $this->assertEqual($link, 'rest_test_relation'); + } + + /** + * Tests that type hooks work as expected even without install hook. + */ + public function testRestLinkManagersNoInstallHook() { + /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ + $type_manager = \Drupal::service('rest.link_manager.type'); + $base = Url::fromRoute('', [], ['absolute' => TRUE])->toString(); + $link = $type_manager->getTypeUri('node', 'page'); + $this->assertEqual($link, $base . 'rest/type/node/page'); + // Now with optional context. + $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); + $this->assertEqual($link, 'rest_test_type'); + + /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ + $relation_manager = \Drupal::service('rest.link_manager.relation'); + $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); + $this->assertEqual($link, $base . 'rest/relation/node/page/field_ref'); + // Now with optional context. + $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); + $this->assertEqual($link, 'rest_test_relation'); + } + + /** + * Tests \Drupal\rest\LinkManager\LinkManager::setLinkDomain(). + */ + public function testRestLinkManagersSetLinkDomain() { + /* @var \Drupal\rest\LinkManager\LinkManager $link_manager */ + $link_manager = \Drupal::service('rest.link_manager'); + $link_manager->setLinkDomain('http://example.com/'); + $link = $link_manager->getTypeUri('node', 'page'); + $this->assertEqual($link, 'http://example.com/rest/type/node/page'); + $link = $link_manager->getRelationUri('node', 'page', 'field_ref'); + $this->assertEqual($link, 'http://example.com/rest/relation/node/page/field_ref'); + } + +} diff --git a/core/modules/rest/tests/modules/rest_test/rest_test.info.yml b/core/modules/rest/tests/modules/rest_test/rest_test.info.yml new file mode 100644 index 00000000000..b5f49669bb9 --- /dev/null +++ b/core/modules/rest/tests/modules/rest_test/rest_test.info.yml @@ -0,0 +1,8 @@ +name: 'REST test' +type: module +description: 'Provides test hooks for REST module.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - rest diff --git a/core/modules/rest/tests/modules/rest_test/rest_test.module b/core/modules/rest/tests/modules/rest_test/rest_test.module new file mode 100644 index 00000000000..272603d157f --- /dev/null +++ b/core/modules/rest/tests/modules/rest_test/rest_test.module @@ -0,0 +1,24 @@ +installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); // Create the test field storage. diff --git a/core/modules/serialization/src/Tests/NormalizerTestBase.php b/core/modules/serialization/src/Tests/NormalizerTestBase.php index 3078f8492be..8ac44856cc2 100644 --- a/core/modules/serialization/src/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/src/Tests/NormalizerTestBase.php @@ -23,8 +23,10 @@ abstract class NormalizerTestBase extends KernelTestBase { $this->installEntitySchema('entity_test_mulrev'); $this->installEntitySchema('user'); - $this->installSchema('system', array('url_alias')); + $this->installSchema('system', array('url_alias', 'router')); $this->installConfig(array('field')); + \Drupal::service('router.builder')->rebuild(); + \Drupal::moduleHandler()->invoke('rest', 'install'); // Auto-create a field for testing. entity_create('field_storage_config', array(