upgrades core to 8.4.2
This commit is contained in:
@@ -7,8 +7,8 @@ package: Web services
|
||||
dependencies:
|
||||
- serialization
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -84,3 +84,39 @@ function rest_update_8203() {
|
||||
$rest_settings->set('bc_entity_resource_permissions', TRUE)
|
||||
->save(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the right REST authentication method is used.
|
||||
*
|
||||
* This fixes the bug in https://www.drupal.org/node/2825204.
|
||||
*/
|
||||
function rest_update_8401() {
|
||||
$config_factory = \Drupal::configFactory();
|
||||
$auth_providers = \Drupal::service('authentication_collector')->getSortedProviders();
|
||||
$process_auth = function ($auth_option) use ($auth_providers) {
|
||||
foreach ($auth_providers as $provider_id => $provider_data) {
|
||||
// The provider belongs to the module that declares it as a service.
|
||||
if (strtok($provider_data->_serviceId, '.') === $auth_option) {
|
||||
return $provider_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $auth_option;
|
||||
};
|
||||
|
||||
foreach ($config_factory->listAll('views.view.') as $view_config_name) {
|
||||
$save = FALSE;
|
||||
$view = $config_factory->getEditable($view_config_name);
|
||||
$displays = $view->get('display');
|
||||
foreach ($displays as $display_name => $display) {
|
||||
if ('rest_export' === $display['display_plugin'] && !empty($display['display_options']['auth'])) {
|
||||
$displays[$display_name]['display_options']['auth'] = array_map($process_auth, $display['display_options']['auth']);
|
||||
$save = TRUE;
|
||||
}
|
||||
}
|
||||
if ($save) {
|
||||
$view->set('display', $displays);
|
||||
$view->save(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
@@ -15,15 +16,42 @@ function rest_help($route_name, RouteMatchInterface $route_match) {
|
||||
case 'help.page.rest':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The RESTful Web Services module provides a framework for exposing REST resources on your site. It provides support for content entities (see the <a href=":field">Field module help page</a> for more information about entities) such as content, users, taxonomy terms, etc.; REST support for content items of the Node module is enabled by default, and support for other types of content entities can be enabled. Other modules may add support for other types of REST resources. For more information, see the <a href=":rest">online documentation for the RESTful Web Services module</a>.', [':rest' => 'https://www.drupal.org/documentation/modules/rest', ':field' => (\Drupal::moduleHandler()->moduleExists('field')) ? \Drupal::url('help.page', ['name' => 'field']) : '#']) . '</p>';
|
||||
$output .= '<p>' . t('The RESTful Web Services module provides a framework for exposing REST resources on your site. It provides support for content entity types such as the main site content, comments, custom blocks, taxonomy terms, and user accounts, etc. (see the <a href=":field">Field module help page</a> for more information about entities). REST support for content items of the Node module is enabled by default, and support for other types of content entities can be enabled. Other modules may add support for other types of REST resources. For more information, see the <a href=":rest">online documentation for the RESTful Web Services module</a>.', [':rest' => 'https://www.drupal.org/documentation/modules/rest', ':field' => (\Drupal::moduleHandler()->moduleExists('field')) ? \Drupal::url('help.page', ['name' => 'field']) : '#']) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Installing supporting modules') . '</dt>';
|
||||
$output .= '<dd>' . t('In order to use REST on a web site, you need to install modules that provide serialization and authentication services. You can use the Core module <a href=":hal">HAL</a> for serialization and <a href=":basic_auth">HTTP Basic Authentication</a> for authentication, or install a contributed or custom module.', [':hal' => (\Drupal::moduleHandler()->moduleExists('hal')) ? \Drupal::url('help.page', ['name' => 'hal']) : '#', ':basic_auth' => (\Drupal::moduleHandler()->moduleExists('basic_auth')) ? \Drupal::url('help.page', ['name' => 'basic_auth']) : '#']) . '</dd>';
|
||||
$output .= '<dt>' . t('Enabling REST support for an entity type') . '</dt>';
|
||||
$output .= '<dd>' . t('REST support for content items of the Node module is enabled by default, and support for other types of content entities can be enabled. To enable support, you can use a <a href=":config">process based on configuration editing</a> or the contributed <a href=":restui">Rest UI module</a>.', [':config' => 'https://www.drupal.org/documentation/modules/rest', ':restui' => 'https://www.drupal.org/project/restui']) . '</dd>';
|
||||
$output .= '<dd>' . t('REST support for content types (provided by the <a href=":node">Node</a> module) is enabled by default. To enable support for other content entity types, you can use a <a href=":config" target="blank">process based on configuration editing</a> or the contributed <a href=":restui">REST UI module</a>.', [':node' => (\Drupal::moduleHandler()->moduleExists('node')) ? \Drupal::url('help.page', ['name' => 'node']) : '#', ':config' => 'https://www.drupal.org/documentation/modules/rest', ':restui' => 'https://www.drupal.org/project/restui']) . '</dd>';
|
||||
$output .= '<dd>' . t('You will also need to grant anonymous users permission to perform each of the REST operations you want to be available, and set up authentication properly to authorize web requests.') . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_view_presave().
|
||||
*
|
||||
* @see rest_update_8401()
|
||||
*/
|
||||
function rest_view_presave(ViewEntityInterface $view) {
|
||||
// Fix the auth options on import, much like what rest_update_8401() does.
|
||||
$auth_providers = \Drupal::service('authentication_collector')->getSortedProviders();
|
||||
$process_auth = function ($auth_option) use ($auth_providers) {
|
||||
foreach ($auth_providers as $provider_id => $provider_data) {
|
||||
// The provider belongs to the module that declares it as a service.
|
||||
if (strtok($provider_data->_serviceId, '.') === $auth_option) {
|
||||
return $provider_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $auth_option;
|
||||
};
|
||||
|
||||
foreach (array_keys($view->get('display')) as $display_id) {
|
||||
$display = &$view->getDisplay($display_id);
|
||||
if ($display['display_plugin'] === 'rest_export' && !empty($display['display_options']['auth'])) {
|
||||
$display['display_options']['auth'] = array_map($process_auth, $display['display_options']['auth']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,3 +31,15 @@ services:
|
||||
arguments: ['@router.builder']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
rest.resource.entity.post_route.subscriber:
|
||||
class: \Drupal\rest\EventSubscriber\EntityResourcePostRouteSubscriber
|
||||
arguments: ['@entity_type.manager']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
# @todo Remove in Drupal 9.0.0.
|
||||
rest.path_processor_entity_resource_bc:
|
||||
class: \Drupal\rest\PathProcessor\PathProcessorEntityResourceBC
|
||||
arguments: ['@entity_type.manager']
|
||||
tags:
|
||||
- { name: path_processor_inbound }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Drupal\rest\Annotation;
|
||||
|
||||
use \Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a REST resource annotation object.
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\rest\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Routing\RouteBuildEvent;
|
||||
use Drupal\Core\Routing\RoutingEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Generates a 'create' route for an entity type if it has a REST POST route.
|
||||
*/
|
||||
class EntityResourcePostRouteSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The REST resource config storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $resourceConfigStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityResourcePostRouteSubscriber instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides routes on route rebuild time.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteBuildEvent $event
|
||||
* The route build event.
|
||||
*/
|
||||
public function onDynamicRouteEvent(RouteBuildEvent $event) {
|
||||
$route_collection = $event->getRouteCollection();
|
||||
|
||||
$resource_configs = $this->resourceConfigStorage->loadMultiple();
|
||||
// Iterate over all REST resource config entities.
|
||||
foreach ($resource_configs as $resource_config) {
|
||||
// We only care about REST resource config entities for the
|
||||
// \Drupal\rest\Plugin\rest\resource\EntityResource plugin.
|
||||
$plugin_id = $resource_config->toArray()['plugin_id'];
|
||||
if (substr($plugin_id, 0, 6) !== 'entity') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entity_type_id = substr($plugin_id, 7);
|
||||
$rest_post_route_name = "rest.entity.$entity_type_id.POST";
|
||||
if ($rest_post_route = $route_collection->get($rest_post_route_name)) {
|
||||
// Create a route for the 'create' link relation type for this entity
|
||||
// type that uses the same route definition as the REST 'POST' route
|
||||
// which use that entity type.
|
||||
// @see \Drupal\Core\Entity\Entity::toUrl()
|
||||
$entity_create_route_name = "entity.$entity_type_id.create";
|
||||
$route_collection->add($entity_create_route_name, $rest_post_route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Priority -10, to run after \Drupal\rest\Routing\ResourceRoutes, which has
|
||||
// priority 0.
|
||||
$events[RoutingEvents::DYNAMIC][] = ['onDynamicRouteEvent', -10];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -96,7 +96,7 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
|
||||
$route = $route_match->getRouteObject();
|
||||
$acceptable_request_formats = $route->hasRequirement('_format') ? explode('|', $route->getRequirement('_format')) : [];
|
||||
$acceptable_content_type_formats = $route->hasRequirement('_content_type_format') ? explode('|', $route->getRequirement('_content_type_format')) : [];
|
||||
$acceptable_formats = $request->isMethodSafe() ? $acceptable_request_formats : $acceptable_content_type_formats;
|
||||
$acceptable_formats = $request->isMethodCacheable() ? $acceptable_request_formats : $acceptable_content_type_formats;
|
||||
|
||||
$requested_format = $request->getRequestFormat();
|
||||
$content_type_format = $request->getContentType();
|
||||
@@ -196,8 +196,9 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Run shortly before \Drupal\Core\EventSubscriber\FinishResponseSubscriber.
|
||||
$events[KernelEvents::RESPONSE][] = ['onResponse', 5];
|
||||
// Run before \Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber
|
||||
// (priority 100), so that Dynamic Page Cache can cache flattened responses.
|
||||
$events[KernelEvents::RESPONSE][] = ['onResponse', 128];
|
||||
return $events;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class RestConfigSubscriber implements EventSubscriberInterface {
|
||||
/**
|
||||
* Constructs the RestConfigSubscriber.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder
|
||||
* @param \Drupal\Core\Routing\RouteBuilderInterface $router_builder
|
||||
* The router builder service.
|
||||
*/
|
||||
public function __construct(RouteBuilderInterface $router_builder) {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\rest\PathProcessor;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Path processor to maintain BC for entity REST resource URLs from Drupal 8.0.
|
||||
*/
|
||||
class PathProcessorEntityResourceBC implements InboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Creates a new PathProcessorEntityResourceBC instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processInbound($path, Request $request) {
|
||||
if ($request->getMethod() === 'POST' && strpos($path, '/entity/') === 0) {
|
||||
$parts = explode('/', $path);
|
||||
$entity_type_id = array_pop($parts);
|
||||
|
||||
// Until Drupal 8.3, no entity types specified a link template for the
|
||||
// 'create' link relation type. As of Drupal 8.3, all core content entity
|
||||
// types provide this link relation type. This inbound path processor
|
||||
// provides automatic backwards compatibility: it allows both the old
|
||||
// default from \Drupal\rest\Plugin\rest\resource\EntityResource, i.e.
|
||||
// "/entity/{entity_type}" and the link template specified in a particular
|
||||
// entity type. The former is rewritten to the latter
|
||||
// specific one if it exists.
|
||||
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
|
||||
if ($entity_type->hasLinkTemplate('create')) {
|
||||
return $entity_type->getLinkTemplate('create');
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class EntityDeriver implements ContainerDeriverInterface {
|
||||
|
||||
$default_uris = [
|
||||
'canonical' => "/entity/$entity_type_id/" . '{' . $entity_type_id . '}',
|
||||
'https://www.drupal.org/link-relations/create' => "/entity/$entity_type_id",
|
||||
'create' => "/entity/$entity_type_id",
|
||||
];
|
||||
|
||||
foreach ($default_uris as $link_relation => $default_uri) {
|
||||
|
||||
@@ -100,7 +100,15 @@ abstract class ResourceBase extends PluginBase implements ContainerFactoryPlugin
|
||||
|
||||
$definition = $this->getPluginDefinition();
|
||||
$canonical_path = isset($definition['uri_paths']['canonical']) ? $definition['uri_paths']['canonical'] : '/' . strtr($this->pluginId, ':', '/') . '/{id}';
|
||||
$create_path = isset($definition['uri_paths']['https://www.drupal.org/link-relations/create']) ? $definition['uri_paths']['https://www.drupal.org/link-relations/create'] : '/' . strtr($this->pluginId, ':', '/');
|
||||
$create_path = isset($definition['uri_paths']['create']) ? $definition['uri_paths']['create'] : '/' . strtr($this->pluginId, ':', '/');
|
||||
// BC: the REST module originally created the POST URL for a resource by
|
||||
// reading the 'https://www.drupal.org/link-relations/create' URI path from
|
||||
// the plugin annotation. For consistency with entity type definitions, that
|
||||
// then changed to reading the 'create' URI path. For any REST Resource
|
||||
// plugins that were using the old mechanism, we continue to support that.
|
||||
if (!isset($definition['uri_paths']['create']) && isset($definition['uri_paths']['https://www.drupal.org/link-relations/create'])) {
|
||||
$create_path = $definition['uri_paths']['https://www.drupal.org/link-relations/create'];
|
||||
}
|
||||
|
||||
$route_name = strtr($this->pluginId, ':', '.');
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
* deriver = "Drupal\rest\Plugin\Deriver\EntityDeriver",
|
||||
* uri_paths = {
|
||||
* "canonical" = "/entity/{entity_type}/{entity}",
|
||||
* "https://www.drupal.org/link-relations/create" = "/entity/{entity_type}"
|
||||
* "create" = "/entity/{entity_type}"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
@@ -431,8 +431,11 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
||||
* @see https://tools.ietf.org/html/rfc5988#section-5
|
||||
*/
|
||||
protected function addLinkHeaders(EntityInterface $entity, Response $response) {
|
||||
foreach ($entity->getEntityType()->getLinkTemplates() as $relation_name => $link_template) {
|
||||
if ($definition = $this->linkRelationTypeManager->getDefinition($relation_name, FALSE)) {
|
||||
foreach ($entity->uriRelationships() as $relation_name) {
|
||||
if ($this->linkRelationTypeManager->hasDefinition($relation_name)) {
|
||||
/** @var \Drupal\Core\Http\LinkRelationTypeInterface $link_relation_type */
|
||||
$link_relation_type = $this->linkRelationTypeManager->createInstance($relation_name);
|
||||
|
||||
$generator_url = $entity->toUrl($relation_name)
|
||||
->setAbsolute(TRUE)
|
||||
->toString(TRUE);
|
||||
@@ -440,10 +443,10 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
||||
$response->addCacheableDependency($generator_url);
|
||||
}
|
||||
$uri = $generator_url->getGeneratedUrl();
|
||||
$relationship = $relation_name;
|
||||
if (!empty($definition['uri'])) {
|
||||
$relationship = $definition['uri'];
|
||||
}
|
||||
|
||||
$relationship = $link_relation_type->isRegistered()
|
||||
? $link_relation_type->getRegisteredName()
|
||||
: $link_relation_type->getExtensionUri();
|
||||
|
||||
$link_header = '<' . $uri . '>; rel="' . $relationship . '"';
|
||||
$response->headers->set('Link', $link_header, FALSE);
|
||||
|
||||
@@ -87,7 +87,23 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
||||
protected $authenticationCollector;
|
||||
|
||||
/**
|
||||
* The authentication providers, keyed by ID.
|
||||
* The authentication providers, like 'cookie' and 'basic_auth'.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $authenticationProviderIds;
|
||||
|
||||
/**
|
||||
* The authentication providers' modules, keyed by provider ID.
|
||||
*
|
||||
* Authentication providers like 'cookie' and 'basic_auth' are the array
|
||||
* keys. The array values are the module names, e.g.:
|
||||
* @code
|
||||
* ['cookie' => 'user', 'basic_auth' => 'basic_auth']
|
||||
* @endcode
|
||||
*
|
||||
* @deprecated as of 8.4.x, will be removed in before Drupal 9.0.0, see
|
||||
* https://www.drupal.org/node/2825204.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
@@ -115,6 +131,13 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state);
|
||||
|
||||
$this->renderer = $renderer;
|
||||
// $authentication_providers as defined in
|
||||
// \Drupal\Core\DependencyInjection\Compiler\AuthenticationProviderPass
|
||||
// and as such it is an array, with authentication providers (cookie,
|
||||
// basic_auth) as keys and modules providing those as values (user,
|
||||
// basic_auth).
|
||||
$this->authenticationProviderIds = array_keys($authentication_providers);
|
||||
// For BC reasons we keep around authenticationProviders as before.
|
||||
$this->authenticationProviders = $authentication_providers;
|
||||
}
|
||||
|
||||
@@ -227,7 +250,7 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
||||
* An array to use as value for "#options" in the form element.
|
||||
*/
|
||||
public function getAuthOptions() {
|
||||
return array_combine($this->authenticationProviders, $this->authenticationProviders);
|
||||
return array_combine($this->authenticationProviderIds, $this->authenticationProviderIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,7 +430,7 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
||||
*/
|
||||
public function render() {
|
||||
$build = [];
|
||||
$build['#markup'] = $this->renderer->executeInRenderContext(new RenderContext(), function() {
|
||||
$build['#markup'] = $this->renderer->executeInRenderContext(new RenderContext(), function () {
|
||||
return $this->view->style_plugin->render();
|
||||
});
|
||||
|
||||
@@ -435,7 +458,7 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
||||
$build['#markup'] = ViewsRenderPipelineMarkup::create($build['#markup']);
|
||||
}
|
||||
|
||||
parent::applyDisplayCachablityMetadata($build);
|
||||
parent::applyDisplayCacheabilityMetadata($build);
|
||||
|
||||
return $build;
|
||||
}
|
||||
@@ -457,10 +480,14 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
||||
$dependencies = parent::calculateDependencies();
|
||||
|
||||
$dependencies += ['module' => []];
|
||||
$modules = array_map(function ($authentication_provider) {
|
||||
return $this->authenticationProviders[$authentication_provider];
|
||||
}, $this->getOption('auth'));
|
||||
$dependencies['module'] = array_merge($dependencies['module'], $modules);
|
||||
$dependencies['module'] = array_merge($dependencies['module'], array_filter(array_map(function ($provider) {
|
||||
// During the update path the provider options might be wrong. This can
|
||||
// happen when any update function, like block_update_8300() triggers a
|
||||
// view to be saved.
|
||||
return isset($this->authenticationProviderIds[$provider])
|
||||
? $this->authenticationProviderIds[$provider]
|
||||
: NULL;
|
||||
}, $this->getOption('auth'))));
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class DataFieldRow extends RowPluginBase {
|
||||
* A regular one dimensional array of values.
|
||||
*/
|
||||
protected static function extractFromOptionsArray($key, $options) {
|
||||
return array_map(function($item) use ($key) {
|
||||
return array_map(function ($item) use ($key) {
|
||||
return isset($item[$key]) ? $item[$key] : NULL;
|
||||
}, $options);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Acts as intermediate request forwarder for resource plugins.
|
||||
@@ -58,22 +60,20 @@ class RequestHandler implements ContainerAwareInterface, ContainerInjectionInter
|
||||
* The response object.
|
||||
*/
|
||||
public function handle(RouteMatchInterface $route_match, Request $request) {
|
||||
$method = strtolower($request->getMethod());
|
||||
|
||||
// Symfony is built to transparently map HEAD requests to a GET request. In
|
||||
// the case of the REST module's RequestHandler though, we essentially have
|
||||
// our own light-weight routing system on top of the Drupal/symfony routing
|
||||
// system. So, we have to do the same as what the UrlMatcher does: map HEAD
|
||||
// requests to the logic for GET. This also guarantees response headers for
|
||||
// HEAD requests are identical to those for GET requests, because we just
|
||||
// return a GET response. Response::prepare() will transform it to a HEAD
|
||||
// response at the very last moment.
|
||||
// system. So, we have to respect the decision that the routing system made:
|
||||
// we look not at the request method, but at the route's method. All REST
|
||||
// routes are guaranteed to have _method set.
|
||||
// Response::prepare() will transform it to a HEAD response at the very last
|
||||
// moment.
|
||||
// @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
|
||||
// @see \Symfony\Component\Routing\Matcher\UrlMatcher::matchCollection()
|
||||
// @see \Symfony\Component\HttpFoundation\Response::prepare()
|
||||
if ($method === 'head') {
|
||||
$method = 'get';
|
||||
}
|
||||
$method = strtolower($route_match->getRouteObject()->getMethods()[0]);
|
||||
assert(count($route_match->getRouteObject()->getMethods()) === 1);
|
||||
|
||||
|
||||
$resource_config_id = $route_match->getRouteObject()->getDefault('_rest_resource_config');
|
||||
/** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
|
||||
@@ -89,19 +89,32 @@ class RequestHandler implements ContainerAwareInterface, ContainerInjectionInter
|
||||
$format = $request->getContentType();
|
||||
|
||||
$definition = $resource->getPluginDefinition();
|
||||
|
||||
// First decode the request data. We can then determine if the
|
||||
// serialized data was malformed.
|
||||
try {
|
||||
if (!empty($definition['serialization_class'])) {
|
||||
$unserialized = $serializer->deserialize($received, $definition['serialization_class'], $format, ['request_method' => $method]);
|
||||
}
|
||||
// If the plugin does not specify a serialization class just decode
|
||||
// the received data.
|
||||
else {
|
||||
$unserialized = $serializer->decode($received, $format, ['request_method' => $method]);
|
||||
}
|
||||
$unserialized = $serializer->decode($received, $format, ['request_method' => $method]);
|
||||
}
|
||||
catch (UnexpectedValueException $e) {
|
||||
// If an exception was thrown at this stage, there was a problem
|
||||
// decoding the data. Throw a 400 http exception.
|
||||
throw new BadRequestHttpException($e->getMessage());
|
||||
}
|
||||
|
||||
// Then attempt to denormalize if there is a serialization class.
|
||||
if (!empty($definition['serialization_class'])) {
|
||||
try {
|
||||
$unserialized = $serializer->denormalize($unserialized, $definition['serialization_class'], $format, ['request_method' => $method]);
|
||||
}
|
||||
// These two serialization exception types mean there was a problem
|
||||
// with the structure of the decoded data and it's not valid.
|
||||
catch (UnexpectedValueException $e) {
|
||||
throw new UnprocessableEntityHttpException($e->getMessage());
|
||||
}
|
||||
catch (InvalidArgumentException $e) {
|
||||
throw new UnprocessableEntityHttpException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the request parameters that should be passed to the resource
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Drupal\rest;
|
||||
|
||||
|
||||
trait ResourceResponseTrait {
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
namespace Drupal\rest\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Drupal\Core\Routing\RouteBuildEvent;
|
||||
use Drupal\Core\Routing\RoutingEvents;
|
||||
use Drupal\rest\Plugin\Type\ResourcePluginManager;
|
||||
use Drupal\rest\RestResourceConfigInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Subscriber for REST-style routes.
|
||||
*/
|
||||
class ResourceRoutes extends RouteSubscriberBase {
|
||||
class ResourceRoutes implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The plugin manager for REST plugins.
|
||||
@@ -54,18 +56,18 @@ class ResourceRoutes extends RouteSubscriberBase {
|
||||
/**
|
||||
* Alters existing routes for a specific collection.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\RouteCollection $collection
|
||||
* The route collection for adding routes.
|
||||
* @param \Drupal\Core\Routing\RouteBuildEvent $event
|
||||
* The route build event.
|
||||
* @return array
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
public function onDynamicRouteEvent(RouteBuildEvent $event) {
|
||||
// Iterate over all enabled REST resource config entities.
|
||||
/** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */
|
||||
$resource_configs = $this->resourceConfigStorage->loadMultiple();
|
||||
foreach ($resource_configs as $resource_config) {
|
||||
if ($resource_config->status()) {
|
||||
$resource_routes = $this->getRoutesForResourceConfig($resource_config);
|
||||
$collection->addCollection($resource_routes);
|
||||
$event->getRouteCollection()->addCollection($resource_routes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,4 +133,12 @@ class ResourceRoutes extends RouteSubscriberBase {
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[RoutingEvents::DYNAMIC] = 'onDynamicRouteEvent';
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -306,10 +306,12 @@ abstract class RESTTestBase extends WebTestBase {
|
||||
return [
|
||||
'name' => $this->randomMachineName(),
|
||||
'user_id' => 1,
|
||||
'field_test_text' => [0 => [
|
||||
'value' => $this->randomString(),
|
||||
'format' => 'plain_text',
|
||||
]],
|
||||
'field_test_text' => [
|
||||
0 => [
|
||||
'value' => $this->randomString(),
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
],
|
||||
];
|
||||
case 'config_test':
|
||||
return [
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\rest\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Ensures that update hook is run properly for REST Export config.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
class RestExportAuthCorrectionUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../tests/fixtures/update/rest-export-with-authentication-correction.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that update hook is run for "rest" module.
|
||||
*/
|
||||
public function testUpdate() {
|
||||
$this->runUpdates();
|
||||
|
||||
// Get particular view.
|
||||
$view = \Drupal::entityTypeManager()->getStorage('view')->load('rest_export_with_authorization_correction');
|
||||
$displays = $view->get('display');
|
||||
$this->assertIdentical($displays['rest_export_1']['display_options']['auth'], ['cookie'], 'Cookie is used for authentication');
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture for \Drupal\rest\Tests\Update\RestExportAuthCorrectionUpdateTest.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
// Set the schema version.
|
||||
$connection->insert('key_value')
|
||||
->fields([
|
||||
'collection' => 'system.schema',
|
||||
'name' => 'rest',
|
||||
'value' => 'i:8000;',
|
||||
])
|
||||
->execute();
|
||||
|
||||
// Update core.extension.
|
||||
$extensions = $connection->select('config')
|
||||
->fields('config', ['data'])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.extension')
|
||||
->execute()
|
||||
->fetchField();
|
||||
$extensions = unserialize($extensions);
|
||||
$extensions['module']['rest'] = 0;
|
||||
$extensions['module']['serialization'] = 0;
|
||||
$extensions['module']['basic_auth'] = 0;
|
||||
$connection->update('config')
|
||||
->fields([
|
||||
'data' => serialize($extensions),
|
||||
])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.extension')
|
||||
->execute();
|
||||
|
||||
$connection->insert('config')
|
||||
->fields([
|
||||
'name' => 'rest.settings',
|
||||
'data' => serialize([
|
||||
'link_domain' => '~',
|
||||
]),
|
||||
'collection' => '',
|
||||
])
|
||||
->execute();
|
||||
|
||||
$connection->insert('config')
|
||||
->fields([
|
||||
'name' => 'views.view.rest_export_with_authorization_correction',
|
||||
])
|
||||
->execute();
|
||||
|
||||
$connection->merge('config')
|
||||
->condition('name', 'views.view.rest_export_with_authorization_correction')
|
||||
->condition('collection', '')
|
||||
->fields([
|
||||
'data' => serialize(Yaml::decode(file_get_contents('core/modules/views/tests/modules/views_test_config/test_views/views.view.rest_export_with_authorization_correction.yml'))),
|
||||
])
|
||||
->execute();
|
||||
@@ -6,8 +6,8 @@ package: Testing
|
||||
dependencies:
|
||||
- config_test
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -7,8 +7,8 @@ package: Testing
|
||||
dependencies:
|
||||
- rest
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -8,8 +8,8 @@ dependencies:
|
||||
- rest
|
||||
- views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional;
|
||||
|
||||
/**
|
||||
* Trait for ResourceTestBase subclasses formatting expected timestamp data.
|
||||
*/
|
||||
trait BcTimestampNormalizerUnixTestTrait {
|
||||
|
||||
/**
|
||||
* Formats a UNIX timestamp.
|
||||
*
|
||||
* Depending on the 'bc_timestamp_normalizer_unix' setting. The return will be
|
||||
* an RFC3339 date string or the same timestamp that was passed in.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The timestamp value to format.
|
||||
*
|
||||
* @return string|int
|
||||
* The formatted RFC3339 date string or UNIX timestamp.
|
||||
*
|
||||
* @see \Drupal\serialization\Normalizer\TimestampItemNormalizer
|
||||
*/
|
||||
protected function formatExpectedTimestampItemValues($timestamp) {
|
||||
// If the setting is enabled, just return the timestamp as-is now.
|
||||
if ($this->config('serialization.settings')->get('bc_timestamp_normalizer_unix')) {
|
||||
return ['value' => $timestamp];
|
||||
}
|
||||
|
||||
// Otherwise, format the date string to the same that
|
||||
// \Drupal\serialization\Normalizer\TimestampItemNormalizer will produce.
|
||||
$date = new \DateTime();
|
||||
$date->setTimestamp($timestamp);
|
||||
$date->setTimezone(new \DateTimeZone('UTC'));
|
||||
|
||||
// Format is also added to the expected return values.
|
||||
return [
|
||||
'value' => $date->format(\DateTime::RFC3339),
|
||||
'format' => \DateTime::RFC3339,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,7 +71,7 @@ trait CookieResourceTestTrait {
|
||||
$this->sessionCookie = explode(';', $response->getHeader('Set-Cookie')[0], 2)[0];
|
||||
|
||||
// Parse and store the CSRF token and logout token.
|
||||
$data = $this->serializer->decode((string)$response->getBody(), static::$format);
|
||||
$data = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
$this->csrfToken = $data['csrf_token'];
|
||||
$this->logoutToken = $data['logout_token'];
|
||||
}
|
||||
|
||||
+1
-3
@@ -122,9 +122,7 @@ abstract class BlockResourceTestBase extends EntityResourceTestBase {
|
||||
protected function getExpectedCacheTags() {
|
||||
// Because the 'user.permissions' cache context is missing, the cache tag
|
||||
// for the anonymous user role is never added automatically.
|
||||
return array_values(array_filter(parent::getExpectedCacheTags(), function ($tag) {
|
||||
return $tag !== 'config:user.role.anonymous';
|
||||
}));
|
||||
return array_values(array_diff(parent::getExpectedCacheTags(), ['config:user.role.anonymous']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+15
-19
@@ -6,13 +6,14 @@ use Drupal\comment\Entity\Comment;
|
||||
use Drupal\comment\Entity\CommentType;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
abstract class CommentResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
use CommentTestTrait, BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -148,14 +149,10 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => 123456789,
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'changed' => [
|
||||
[
|
||||
'value' => $this->entity->getChangedTime(),
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
@@ -222,7 +219,7 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
'entity_id' => [
|
||||
[
|
||||
'target_id' => EntityTest::load(1)->id(),
|
||||
'target_id' => (int) EntityTest::load(1)->id(),
|
||||
],
|
||||
],
|
||||
'field_name' => [
|
||||
@@ -280,9 +277,9 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
// @todo Uncomment, remove next 3 lines in https://www.drupal.org/node/2820364.
|
||||
$this->assertSame(500, $response->getStatusCode());
|
||||
$this->assertSame(['application/json'], $response->getHeader('Content-Type'));
|
||||
$this->assertSame('{"message":"A fatal error occurred: Internal Server Error"}', (string) $response->getBody());
|
||||
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
|
||||
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
$this->assertStringStartsWith('The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Symfony\Component\HttpKernel\Exception\HttpException</em>: Internal Server Error in <em class="placeholder">Drupal\rest\Plugin\rest\resource\EntityResource->post()</em>', (string) $response->getBody());
|
||||
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
|
||||
|
||||
// DX: 422 when missing 'entity_id' field.
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_id' => TRUE]), static::$format);
|
||||
@@ -291,23 +288,22 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
|
||||
try {
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
// This happens on DrupalCI.
|
||||
//$this->assertSame(500, $response->getStatusCode());
|
||||
// $this->assertSame(500, $response->getStatusCode());
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// This happens on Wim's local machine.
|
||||
//$this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage());
|
||||
// $this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage());
|
||||
}
|
||||
//$response = $this->request('POST', $url, $request_options);
|
||||
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
|
||||
// $response = $this->request('POST', $url, $request_options);
|
||||
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
|
||||
|
||||
// DX: 422 when missing 'entity_type' field.
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['field_name' => TRUE]), static::$format);
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
// @todo Uncomment, remove next 3 lines in https://www.drupal.org/node/2820364.
|
||||
// @todo Uncomment, remove next 2 lines in https://www.drupal.org/node/2820364.
|
||||
$this->assertSame(500, $response->getStatusCode());
|
||||
$this->assertSame(['application/json'], $response->getHeader('Content-Type'));
|
||||
$this->assertSame('{"message":"A fatal error occurred: Field is unknown."}', (string) $response->getBody());
|
||||
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response);
|
||||
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormJsonAnonTest extends ContactFormResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormJsonBasicAuthTest extends ContactFormResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormJsonCookieTest extends ContactFormResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
abstract class ContactFormResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'contact_form';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [];
|
||||
|
||||
/**
|
||||
* @var \Drupal\contact\Entity\ContactForm
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['access site-wide contact form']);
|
||||
default:
|
||||
$this->grantPermissionsToTestedRole(['administer contact forms']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$contact_form = ContactForm::create([
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
]);
|
||||
$contact_form->save();
|
||||
|
||||
return $contact_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'langcode' => 'en',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
'redirect' => NULL,
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'weight' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'access site-wide contact form' permission is required.";
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class DateFormatJsonAnonTest extends DateFormatResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class DateFormatJsonBasicAuthTest extends DateFormatResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class DateFormatJsonCookieTest extends DateFormatResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
|
||||
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for DateFormat entity.
|
||||
*/
|
||||
abstract class DateFormatResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'date_format';
|
||||
|
||||
/**
|
||||
* The DateFormat entity.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer site configuration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a date format.
|
||||
$date_format = DateFormat::create([
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'pattern' => 'F d, Y',
|
||||
]);
|
||||
|
||||
$date_format->save();
|
||||
|
||||
return $date_format;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'langcode' => 'en',
|
||||
'locked' => FALSE,
|
||||
'pattern' => 'F d, Y',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityFormModeJsonAnonTest extends EntityFormModeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityFormModeJsonBasicAuthTest extends EntityFormModeResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityFormModeJsonCookieTest extends EntityFormModeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\Core\Entity\Entity\EntityFormMode;
|
||||
|
||||
abstract class EntityFormModeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo: Remove 'field_ui' when https://www.drupal.org/node/2867266.
|
||||
*/
|
||||
public static $modules = ['user', 'field_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'entity_form_mode';
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityFormModeInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer display modes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$entity_form_mode = EntityFormMode::create([
|
||||
'id' => 'user.test',
|
||||
'label' => 'Test',
|
||||
'targetEntityType' => 'user',
|
||||
]);
|
||||
$entity_form_mode->save();
|
||||
return $entity_form_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'cache' => TRUE,
|
||||
'dependencies' => [
|
||||
'module' => [
|
||||
'user',
|
||||
],
|
||||
],
|
||||
'id' => 'user.test',
|
||||
'label' => 'Test',
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'targetEntityType' => 'user',
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,11 +4,13 @@ namespace Drupal\Tests\rest\Functional\EntityResource;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableResponseInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\rest\ResourceResponseInterface;
|
||||
use Drupal\Tests\rest\Functional\ResourceTestBase;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@@ -149,7 +151,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
// Calculate REST Resource config entity ID.
|
||||
static::$resourceConfigId = 'entity.' . static::$entityTypeId;
|
||||
|
||||
$this->serializer = $this->container->get('serializer');
|
||||
$this->entityStorage = $this->container->get('entity_type.manager')
|
||||
->getStorage(static::$entityTypeId);
|
||||
|
||||
@@ -229,7 +230,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return $this->getExpectedBCUnauthorizedAccessMessage($method);
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
$permission = $this->entity->getEntityType()->getAdminPermission();
|
||||
@@ -253,13 +254,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
return "$message.";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedBcUnauthorizedAccessMessage($method) {
|
||||
return "The 'restful " . strtolower($method) . " entity:" . $this->entity->getEntityTypeId() . "' permission is required.";
|
||||
}
|
||||
|
||||
/**
|
||||
* The expected cache tags for the GET/HEAD response of the test entity.
|
||||
*
|
||||
@@ -334,7 +328,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$url->setOption('query', []);
|
||||
|
||||
|
||||
|
||||
// DX: 406 when ?_format is missing, except when requesting a canonical HTML
|
||||
// route.
|
||||
$response = $this->request('GET', $url, $request_options);
|
||||
@@ -379,13 +372,24 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$this->assertArrayNotHasKey('Link', $response->getHeaders());
|
||||
|
||||
|
||||
|
||||
$this->setUpAuthorization('GET');
|
||||
|
||||
|
||||
// 200 for well-formed HEAD request.
|
||||
$response = $this->request('HEAD', $url, $request_options);
|
||||
$this->assertResourceResponse(200, '', $response);
|
||||
// @todo Entity resources with URLs that begin with '/admin/' are marked as
|
||||
// administrative (see https://www.drupal.org/node/2874938), which
|
||||
// excludes them from Dynamic Page Cache (see
|
||||
// https://www.drupal.org/node/2877528). When either of those issues is
|
||||
// fixed, remove the if-test and the 'else' block.
|
||||
if (strpos($this->entity->getEntityType()->getLinkTemplate('canonical'), '/admin/') !== 0) {
|
||||
$this->assertTrue($response->hasHeader('X-Drupal-Dynamic-Cache'));
|
||||
$this->assertSame(['MISS'], $response->getHeader('X-Drupal-Dynamic-Cache'));
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Dynamic-Cache'));
|
||||
}
|
||||
if (!$this->account) {
|
||||
$this->assertSame(['MISS'], $response->getHeader('X-Drupal-Cache'));
|
||||
}
|
||||
@@ -395,13 +399,53 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$head_headers = $response->getHeaders();
|
||||
|
||||
// 200 for well-formed GET request. Page Cache hit because of HEAD request.
|
||||
// Same for Dynamic Page Cache hit.
|
||||
$response = $this->request('GET', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
if (!static::$auth) {
|
||||
$this->assertSame(['HIT'], $response->getHeader('X-Drupal-Cache'));
|
||||
// @todo Entity resources with URLs that begin with '/admin/' are marked as
|
||||
// administrative (see https://www.drupal.org/node/2874938), which
|
||||
// excludes them from Dynamic Page Cache (see
|
||||
// https://www.drupal.org/node/2877528). When either of those issues is
|
||||
// fixed, remove the if-test and the 'else' block.
|
||||
if (strpos($this->entity->getEntityType()->getLinkTemplate('canonical'), '/admin/') !== 0) {
|
||||
$this->assertTrue($response->hasHeader('X-Drupal-Dynamic-Cache'));
|
||||
if (!static::$auth) {
|
||||
$this->assertSame(['HIT'], $response->getHeader('X-Drupal-Cache'));
|
||||
$this->assertSame(['MISS'], $response->getHeader('X-Drupal-Dynamic-Cache'));
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
|
||||
$this->assertSame(['HIT'], $response->getHeader('X-Drupal-Dynamic-Cache'));
|
||||
// Assert that Dynamic Page Cache did not store a ResourceResponse object,
|
||||
// which needs serialization after every cache hit. Instead, it should
|
||||
// contain a flattened response. Otherwise performance suffers.
|
||||
// @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber::flattenResponse()
|
||||
$cache_items = $this->container->get('database')
|
||||
->query("SELECT cid, data FROM {cache_dynamic_page_cache} WHERE cid LIKE :pattern", [
|
||||
':pattern' => '%[route]=rest.%',
|
||||
])
|
||||
->fetchAllAssoc('cid');
|
||||
$this->assertCount(2, $cache_items);
|
||||
$found_cache_redirect = FALSE;
|
||||
$found_cached_response = FALSE;
|
||||
foreach ($cache_items as $cid => $cache_item) {
|
||||
$cached_data = unserialize($cache_item->data);
|
||||
if (!isset($cached_data['#cache_redirect'])) {
|
||||
$found_cached_response = TRUE;
|
||||
$cached_response = $cached_data['#response'];
|
||||
$this->assertNotInstanceOf(ResourceResponseInterface::class, $cached_response);
|
||||
$this->assertInstanceOf(CacheableResponseInterface::class, $cached_response);
|
||||
}
|
||||
else {
|
||||
$found_cache_redirect = TRUE;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found_cache_redirect);
|
||||
$this->assertTrue($found_cached_response);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Dynamic-Cache'));
|
||||
}
|
||||
$cache_tags_header_value = $response->getHeader('X-Drupal-Cache-Tags')[0];
|
||||
$this->assertEquals($this->getExpectedCacheTags(), empty($cache_tags_header_value) ? [] : explode(' ', $cache_tags_header_value));
|
||||
@@ -411,9 +455,9 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
// for the keys with the array order the same (it needs to match with
|
||||
// identical comparison).
|
||||
$expected = $this->getExpectedNormalizedEntity();
|
||||
ksort($expected);
|
||||
static::recursiveKSort($expected);
|
||||
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
ksort($actual);
|
||||
static::recursiveKSort($actual);
|
||||
$this->assertSame($expected, $actual);
|
||||
|
||||
// Not only assert the normalization, also assert deserialization of the
|
||||
@@ -424,11 +468,11 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
if ($this->entity->getEntityType()->getLinkTemplates()) {
|
||||
$this->assertArrayHasKey('Link', $response->getHeaders());
|
||||
$link_relation_type_manager = $this->container->get('plugin.manager.link_relation_type');
|
||||
$expected_link_relation_headers = array_map(function ($rel) use ($link_relation_type_manager) {
|
||||
$definition = $link_relation_type_manager->getDefinition($rel, FALSE);
|
||||
return (!empty($definition['uri']))
|
||||
? $definition['uri']
|
||||
: $rel;
|
||||
$expected_link_relation_headers = array_map(function ($relation_name) use ($link_relation_type_manager) {
|
||||
$link_relation_type = $link_relation_type_manager->createInstance($relation_name);
|
||||
return $link_relation_type->isRegistered()
|
||||
? $link_relation_type->getRegisteredName()
|
||||
: $link_relation_type->getExtensionUri();
|
||||
}, array_keys($this->entity->getEntityType()->getLinkTemplates()));
|
||||
$parse_rel_from_link_header = function ($value) use ($link_relation_type_manager) {
|
||||
$matches = [];
|
||||
@@ -453,12 +497,14 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
}
|
||||
$this->assertSame($get_headers, $head_headers);
|
||||
|
||||
// BC: serialization_update_8302().
|
||||
// Only run this for fieldable entities. It doesn't make sense for config
|
||||
// entities as config values are already casted. They also run through the
|
||||
// ConfigEntityNormalizer, which doesn't deal with fields individually.
|
||||
if ($this->entity instanceof FieldableEntityInterface) {
|
||||
// Test primitive data casting BC (strings).
|
||||
$this->config('serialization.settings')->set('bc_primitives_as_strings', TRUE)->save(TRUE);
|
||||
// Rebuild the container so new config is reflected in the removal of the
|
||||
// Rebuild the container so new config is reflected in the addition of the
|
||||
// PrimitiveDataNormalizer.
|
||||
$this->rebuildAll();
|
||||
|
||||
@@ -474,10 +520,48 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
// Config entities are not affected.
|
||||
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
|
||||
$expected = static::castToString($expected);
|
||||
ksort($expected);
|
||||
static::recursiveKSort($expected);
|
||||
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
ksort($actual);
|
||||
static::recursiveKSort($actual);
|
||||
$this->assertSame($expected, $actual);
|
||||
|
||||
// Reset the config value and rebuild.
|
||||
$this->config('serialization.settings')->set('bc_primitives_as_strings', FALSE)->save(TRUE);
|
||||
$this->rebuildAll();
|
||||
}
|
||||
|
||||
// BC: serialization_update_8401().
|
||||
// Only run this for fieldable entities. It doesn't make sense for config
|
||||
// entities as config values always use the raw values (as per the config
|
||||
// schema), returned directly from the ConfigEntityNormalizer, which
|
||||
// doesn't deal with fields individually.
|
||||
if ($this->entity instanceof FieldableEntityInterface) {
|
||||
// Test the BC settings for timestamp values.
|
||||
$this->config('serialization.settings')->set('bc_timestamp_normalizer_unix', TRUE)->save(TRUE);
|
||||
// Rebuild the container so new config is reflected in the addition of the
|
||||
// TimestampItemNormalizer.
|
||||
$this->rebuildAll();
|
||||
|
||||
|
||||
$response = $this->request('GET', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
|
||||
|
||||
// This ensures the BC layer for bc_timestamp_normalizer_unix works as
|
||||
// expected. This method should be using
|
||||
// ::formatExpectedTimestampValue() to generate the timestamp value. This
|
||||
// will take into account the above config setting.
|
||||
$expected = $this->getExpectedNormalizedEntity();
|
||||
// Config entities are not affected.
|
||||
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
|
||||
static::recursiveKSort($expected);
|
||||
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
static::recursiveKSort($actual);
|
||||
$this->assertSame($expected, $actual);
|
||||
|
||||
// Reset the config value and rebuild.
|
||||
$this->config('serialization.settings')->set('bc_timestamp_normalizer_unix', FALSE)->save(TRUE);
|
||||
$this->rebuildAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -531,17 +615,17 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
// DX: 406 when requesting unsupported format.
|
||||
$response = $this->request('GET', $url, $request_options);
|
||||
$this->assert406Response($response);
|
||||
$this->assertNotSame([static::$mimeType], $response->getHeader('Content-Type'));
|
||||
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
|
||||
|
||||
$request_options[RequestOptions::HEADERS]['Accept'] = static::$mimeType;
|
||||
|
||||
|
||||
// DX: 406 when requesting unsupported format but specifying Accept header.
|
||||
// @todo Update in https://www.drupal.org/node/2825347.
|
||||
// DX: 406 when requesting unsupported format but specifying Accept header:
|
||||
// should result in a text/plain response.
|
||||
$response = $this->request('GET', $url, $request_options);
|
||||
$this->assert406Response($response);
|
||||
$this->assertSame(['application/json'], $response->getHeader('Content-Type'));
|
||||
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
|
||||
|
||||
$url = Url::fromRoute('rest.entity.' . static::$entityTypeId . '.GET.' . static::$format);
|
||||
@@ -580,6 +664,27 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
return $normalization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sorts an array by key.
|
||||
*
|
||||
* @param array $array
|
||||
* An array to sort.
|
||||
*
|
||||
* @return array
|
||||
* The sorted array.
|
||||
*/
|
||||
protected static function recursiveKSort(array &$array) {
|
||||
// First, sort the main array.
|
||||
ksort($array);
|
||||
|
||||
// Then check for child arrays.
|
||||
foreach ($array as $key => &$value) {
|
||||
if (is_array($value)) {
|
||||
static::recursiveKSort($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a POST request for an entity, plus edge cases to ensure good DX.
|
||||
*/
|
||||
@@ -635,7 +740,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
$this->assertSame(415, $response->getStatusCode());
|
||||
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
$this->assertContains(htmlspecialchars('No "Content-Type" request header specified'), (string) $response->getBody());
|
||||
$this->assertContains('A client error happened', (string) $response->getBody());
|
||||
|
||||
|
||||
$url->setOption('query', ['_format' => static::$format]);
|
||||
@@ -741,6 +846,25 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$this->assertSame([], $response->getHeader('Location'));
|
||||
}
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
|
||||
// Assert that the entity was indeed created, and that the response body
|
||||
// contains the serialized created entity.
|
||||
$created_entity = $this->entityStorage->loadUnchanged(static::$firstCreatedEntityId);
|
||||
$created_entity_normalization = $this->serializer->normalize($created_entity, static::$format, ['account' => $this->account]);
|
||||
// @todo Remove this if-test in https://www.drupal.org/node/2543726: execute
|
||||
// its body unconditionally.
|
||||
if (static::$entityTypeId !== 'taxonomy_term') {
|
||||
$this->assertSame($created_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
|
||||
}
|
||||
// Assert that the entity was indeed created using the POSTed values.
|
||||
foreach ($this->getNormalizedPostEntity() as $field_name => $field_normalization) {
|
||||
// Some top-level keys in the normalization may not be fields on the
|
||||
// entity (for example '_links' and '_embedded' in the HAL normalization).
|
||||
if ($created_entity->hasField($field_name)) {
|
||||
// Subset, not same, because we can e.g. send just the target_id for the
|
||||
// bundle in a POST request; the response will include more properties.
|
||||
$this->assertArraySubset(static::castToString($field_normalization), $created_entity->get($field_name)->getValue(), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
|
||||
@@ -769,6 +893,17 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$this->assertSame([], $response->getHeader('Location'));
|
||||
}
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
|
||||
|
||||
// BC: old default POST URLs have their path updated by the inbound path
|
||||
// processor \Drupal\rest\PathProcessor\PathProcessorEntityResourceBC to the
|
||||
// new URL, which is derived from the 'create' link template if an entity
|
||||
// type specifies it.
|
||||
if ($this->entity->getEntityType()->hasLinkTemplate('create')) {
|
||||
$this->entityStorage->load(static::$secondCreatedEntityId)->delete();
|
||||
$old_url = Url::fromUri('base:entity/' . static::$entityTypeId);
|
||||
$response = $this->request('POST', $old_url, $request_options);
|
||||
$this->assertResourceResponse(201, FALSE, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,6 +942,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$this->assertSame(405, $response->getStatusCode());
|
||||
$this->assertSame(['GET, POST, HEAD'], $response->getHeader('Allow'));
|
||||
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
$this->assertContains('A client error happened', (string) $response->getBody());
|
||||
}
|
||||
else {
|
||||
$this->assertSame(404, $response->getStatusCode());
|
||||
@@ -836,7 +972,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertSame(415, $response->getStatusCode());
|
||||
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
$this->assertTrue(FALSE !== strpos((string) $response->getBody(), htmlspecialchars('No "Content-Type" request header specified')));
|
||||
$this->assertContains('A client error happened', (string) $response->getBody());
|
||||
|
||||
|
||||
$url->setOption('query', ['_format' => static::$format]);
|
||||
@@ -863,7 +999,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$this->assertResourceErrorResponse(400, 'Syntax error', $response);
|
||||
|
||||
|
||||
|
||||
$request_options[RequestOptions::BODY] = $parseable_invalid_request_body;
|
||||
|
||||
|
||||
@@ -945,10 +1080,25 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
|
||||
// Assert that the entity was indeed updated, and that the response body
|
||||
// contains the serialized updated entity.
|
||||
$updated_entity = $this->entityStorage->loadUnchanged($this->entity->id());
|
||||
$updated_entity_normalization = $this->serializer->normalize($updated_entity, static::$format, ['account' => $this->account]);
|
||||
$this->assertSame($updated_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
|
||||
// Assert that the entity was indeed created using the PATCHed values.
|
||||
foreach ($this->getNormalizedPatchEntity() as $field_name => $field_normalization) {
|
||||
// Some top-level keys in the normalization may not be fields on the
|
||||
// entity (for example '_links' and '_embedded' in the HAL normalization).
|
||||
if ($updated_entity->hasField($field_name)) {
|
||||
// Subset, not same, because we can e.g. send just the target_id for the
|
||||
// bundle in a PATCH request; the response will include more properties.
|
||||
$this->assertArraySubset(static::castToString($field_normalization), $updated_entity->get($field_name)->getValue(), TRUE);
|
||||
}
|
||||
}
|
||||
// Ensure that fields do not get deleted if they're not present in the PATCH
|
||||
// request. Test this using the configurable field that we added, but which
|
||||
// is not sent in the PATCH request.
|
||||
$this->assertSame('All the faith he had had had had no effect on the outcome of his life.', $this->entityStorage->loadUnchanged($this->entity->id())->get('field_rest_test')->value);
|
||||
$this->assertSame('All the faith he had had had had no effect on the outcome of his life.', $updated_entity->get('field_rest_test')->value);
|
||||
|
||||
|
||||
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
|
||||
@@ -992,13 +1142,14 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$request_options = [];
|
||||
|
||||
|
||||
// DX: 405 when resource not provisioned, but HTML if canonical route. Plain
|
||||
// DX: 404 when resource not provisioned, but 405 if canonical route. Plain
|
||||
// text or HTML response because missing ?_format query string.
|
||||
$response = $this->request('DELETE', $url, $request_options);
|
||||
if ($has_canonical_url) {
|
||||
$this->assertSame(405, $response->getStatusCode());
|
||||
$this->assertSame(['GET, POST, HEAD'], $response->getHeader('Allow'));
|
||||
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
|
||||
$this->assertContains('A client error happened', (string) $response->getBody());
|
||||
}
|
||||
else {
|
||||
$this->assertSame(404, $response->getStatusCode());
|
||||
@@ -1099,10 +1250,9 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
|
||||
// DX: 400 when incorrect entity type bundle is specified.
|
||||
// @todo Change to 422 in https://www.drupal.org/node/2827084.
|
||||
// DX: 422 when incorrect entity type bundle is specified.
|
||||
$response = $this->request($method, $url, $request_options);
|
||||
$this->assertResourceErrorResponse(400, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response);
|
||||
$this->assertResourceErrorResponse(422, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response);
|
||||
}
|
||||
|
||||
|
||||
@@ -1110,10 +1260,9 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
|
||||
// DX: 400 when no entity type bundle is specified.
|
||||
// @todo Change to 422 in https://www.drupal.org/node/2827084.
|
||||
// DX: 422 when no entity type bundle is specified.
|
||||
$response = $this->request($method, $url, $request_options);
|
||||
$this->assertResourceErrorResponse(400, sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_field_name), $response);
|
||||
$this->assertResourceErrorResponse(422, sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_field_name), $response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,7 +1274,10 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
*/
|
||||
protected function getEntityResourceUrl() {
|
||||
$has_canonical_url = $this->entity->hasLinkTemplate('canonical');
|
||||
return $has_canonical_url ? $this->entity->toUrl() : Url::fromUri('base:entity/' . static::$entityTypeId . '/' . $this->entity->id());
|
||||
// Note that the 'canonical' link relation type must be specified explicitly
|
||||
// in the call to ::toUrl(). 'canonical' is the default for
|
||||
// \Drupal\Core\Entity\Entity::toUrl(), but ConfigEntityBase overrides this.
|
||||
return $has_canonical_url ? $this->entity->toUrl('canonical') : Url::fromUri('base:entity/' . static::$entityTypeId . '/' . $this->entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1135,8 +1287,8 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||
* The URL to POST to.
|
||||
*/
|
||||
protected function getEntityResourcePostUrl() {
|
||||
$has_canonical_url = $this->entity->hasLinkTemplate('https://www.drupal.org/link-relations/create');
|
||||
return $has_canonical_url ? $this->entity->toUrl() : Url::fromUri('base:entity/' . static::$entityTypeId);
|
||||
$has_create_url = $this->entity->hasLinkTemplate('create');
|
||||
return $has_create_url ? Url::fromUri('internal:' . $this->entity->getEntityType()->getLinkTemplate('create')) : Url::fromUri('base:entity/' . static::$entityTypeId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-4
@@ -3,11 +3,14 @@
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -92,9 +95,7 @@ abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
|
||||
]
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => (int) $this->entity->get('created')->value,
|
||||
]
|
||||
$this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value)
|
||||
],
|
||||
'user_id' => [
|
||||
[
|
||||
@@ -115,7 +116,11 @@ abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'type' => 'entity_test',
|
||||
'type' => [
|
||||
[
|
||||
'value' => 'entity_test',
|
||||
],
|
||||
],
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Dramallama',
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityTestBundleJsonAnonTest extends EntityTestBundleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityTestBundleJsonBasicAuthTest extends EntityTestBundleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityTestBundleJsonCookieTest extends EntityTestBundleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestBundle;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
abstract class EntityTestBundleResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'entity_test_bundle';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [];
|
||||
|
||||
/**
|
||||
* @var \Drupal\entity_test\Entity\EntityTestBundle
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer entity_test_bundle content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$entity_test_bundle = EntityTestBundle::create([
|
||||
'id' => 'camelids',
|
||||
'label' => 'Camelids',
|
||||
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
|
||||
]);
|
||||
$entity_test_bundle->save();
|
||||
|
||||
return $entity_test_bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
|
||||
'id' => 'camelids',
|
||||
'label' => 'Camelids',
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
||||
+9
-4
@@ -3,11 +3,14 @@
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestLabel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestLabel;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
abstract class EntityTestLabelResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -94,9 +97,7 @@ abstract class EntityTestLabelResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => (int) $this->entity->get('created')->value,
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value),
|
||||
],
|
||||
'user_id' => [
|
||||
[
|
||||
@@ -116,7 +117,11 @@ abstract class EntityTestLabelResourceTestBase extends EntityResourceTestBase {
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'type' => 'entity_test_label',
|
||||
'type' => [
|
||||
[
|
||||
'value' => 'entity_test_label',
|
||||
],
|
||||
],
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'label_llama',
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityViewModeJsonAnonTest extends EntityViewModeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityViewModeJsonBasicAuthTest extends EntityViewModeResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityViewModeJsonCookieTest extends EntityViewModeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\Core\Entity\Entity\EntityViewMode;
|
||||
|
||||
abstract class EntityViewModeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo: Remove 'field_ui' when https://www.drupal.org/node/2867266.
|
||||
*/
|
||||
public static $modules = ['user', 'field_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'entity_view_mode';
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityViewModeInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer display modes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$entity_view_mode = EntityViewMode::create([
|
||||
'id' => 'user.test',
|
||||
'label' => 'Test',
|
||||
'targetEntityType' => 'user',
|
||||
]);
|
||||
$entity_view_mode->save();
|
||||
return $entity_view_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'cache' => TRUE,
|
||||
'dependencies' => [
|
||||
'module' => [
|
||||
'user',
|
||||
],
|
||||
],
|
||||
'id' => 'user.test',
|
||||
'label' => 'Test',
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'targetEntityType' => 'user',
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Feed;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestResourceTestBase;
|
||||
use Drupal\aggregator\Entity\Feed;
|
||||
|
||||
abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -92,14 +95,10 @@ abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
|
||||
]
|
||||
],
|
||||
'checked' => [
|
||||
[
|
||||
'value' => 123456789
|
||||
]
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'queued' => [
|
||||
[
|
||||
'value' => 123456789
|
||||
]
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'link' => [
|
||||
[
|
||||
@@ -127,9 +126,7 @@ abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
|
||||
]
|
||||
],
|
||||
'modified' => [
|
||||
[
|
||||
'value' => 123456789
|
||||
]
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Item;
|
||||
|
||||
use Drupal\aggregator\Entity\Feed;
|
||||
use Drupal\aggregator\Entity\Item;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
@@ -11,6 +12,8 @@ use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
*/
|
||||
abstract class ItemResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -113,9 +116,7 @@ abstract class ItemResourceTestBase extends EntityResourceTestBase {
|
||||
'author' => [],
|
||||
'description' => [],
|
||||
'timestamp' => [
|
||||
[
|
||||
'value' => 123456789,
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'guid' => [],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MediaJsonAnonTest extends MediaResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MediaJsonBasicAuthTest extends MediaResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MediaJsonCookieTest extends MediaResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\media\Entity\Media;
|
||||
use Drupal\media\Entity\MediaType;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
abstract class MediaResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['media'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'media';
|
||||
|
||||
/**
|
||||
* @var \Drupal\media\MediaInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'changed',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['view media']);
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$this->grantPermissionsToTestedRole(['create media']);
|
||||
break;
|
||||
|
||||
case 'PATCH':
|
||||
$this->grantPermissionsToTestedRole(['update any media']);
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
$this->grantPermissionsToTestedRole(['delete any media']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
if (!MediaType::load('camelids')) {
|
||||
// Create a "Camelids" media type.
|
||||
$media_type = MediaType::create([
|
||||
'name' => 'Camelids',
|
||||
'id' => 'camelids',
|
||||
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
|
||||
'source' => 'file',
|
||||
]);
|
||||
$media_type->save();
|
||||
// Create the source field.
|
||||
$source_field = $media_type->getSource()->createSourceField($media_type);
|
||||
$source_field->getFieldStorageDefinition()->save();
|
||||
$source_field->save();
|
||||
$media_type
|
||||
->set('source_configuration', [
|
||||
'source_field' => $source_field->getName(),
|
||||
])
|
||||
->save();
|
||||
}
|
||||
|
||||
// Create a file to upload.
|
||||
$file = File::create([
|
||||
'uri' => 'public://llama.txt',
|
||||
]);
|
||||
$file->setPermanent();
|
||||
$file->save();
|
||||
|
||||
// Create a "Llama" media item.
|
||||
$media = Media::create([
|
||||
'bundle' => 'camelids',
|
||||
'field_media_file_1' => [
|
||||
'target_id' => $file->id(),
|
||||
],
|
||||
]);
|
||||
$media
|
||||
->setName('Llama')
|
||||
->setPublished(TRUE)
|
||||
->setCreatedTime(123456789)
|
||||
->setOwnerId(static::$auth ? $this->account->id() : 0)
|
||||
->setRevisionUserId(static::$auth ? $this->account->id() : 0)
|
||||
->save();
|
||||
|
||||
return $media;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$file = File::load(1);
|
||||
$thumbnail = File::load(2);
|
||||
$author = User::load($this->entity->getOwnerId());
|
||||
return [
|
||||
'mid' => [
|
||||
[
|
||||
'value' => 1,
|
||||
],
|
||||
],
|
||||
'uuid' => [
|
||||
[
|
||||
'value' => $this->entity->uuid(),
|
||||
],
|
||||
],
|
||||
'vid' => [
|
||||
[
|
||||
'value' => 1,
|
||||
],
|
||||
],
|
||||
'langcode' => [
|
||||
[
|
||||
'value' => 'en',
|
||||
],
|
||||
],
|
||||
'bundle' => [
|
||||
[
|
||||
'target_id' => 'camelids',
|
||||
'target_type' => 'media_type',
|
||||
'target_uuid' => MediaType::load('camelids')->uuid(),
|
||||
],
|
||||
],
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Llama',
|
||||
],
|
||||
],
|
||||
'field_media_file_1' => [
|
||||
[
|
||||
'description' => NULL,
|
||||
'display' => NULL,
|
||||
'target_id' => (int) $file->id(),
|
||||
'target_type' => 'file',
|
||||
'target_uuid' => $file->uuid(),
|
||||
'url' => $file->url(),
|
||||
],
|
||||
],
|
||||
'thumbnail' => [
|
||||
[
|
||||
'alt' => 'Thumbnail',
|
||||
'width' => 180,
|
||||
'height' => 180,
|
||||
'target_id' => (int) $thumbnail->id(),
|
||||
'target_type' => 'file',
|
||||
'target_uuid' => $thumbnail->uuid(),
|
||||
'title' => 'Llama',
|
||||
'url' => $thumbnail->url(),
|
||||
],
|
||||
],
|
||||
'status' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'changed' => [
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'revision_created' => [
|
||||
$this->formatExpectedTimestampItemValues((int) $this->entity->getRevisionCreationTime()),
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'uid' => [
|
||||
[
|
||||
'target_id' => (int) $author->id(),
|
||||
'target_type' => 'user',
|
||||
'target_uuid' => $author->uuid(),
|
||||
'url' => base_path() . 'user/' . $author->id(),
|
||||
],
|
||||
],
|
||||
'revision_user' => [
|
||||
[
|
||||
'target_id' => (int) $author->id(),
|
||||
'target_type' => 'user',
|
||||
'target_uuid' => $author->uuid(),
|
||||
'url' => base_path() . 'user/' . $author->id(),
|
||||
],
|
||||
],
|
||||
'revision_log_message' => [],
|
||||
'revision_translation_affected' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'bundle' => [
|
||||
[
|
||||
'target_id' => 'camelids',
|
||||
],
|
||||
],
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Dramallama',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case 'GET';
|
||||
return "The 'view media' permission is required and the media item must be published.";
|
||||
|
||||
case 'PATCH':
|
||||
return 'You are not authorized to update this media entity of bundle camelids.';
|
||||
|
||||
case 'DELETE':
|
||||
return 'You are not authorized to delete this media entity of bundle camelids.';
|
||||
|
||||
default:
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPost() {
|
||||
$this->markTestSkipped('POSTing File Media items is not supported until https://www.drupal.org/node/1927648 is solved.');
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MediaTypeJsonAnonTest extends MediaTypeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MediaTypeJsonBasicAuthTest extends MediaTypeResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MediaTypeJsonCookieTest extends MediaTypeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
|
||||
|
||||
use Drupal\media\Entity\MediaType;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
abstract class MediaTypeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['media'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'media_type';
|
||||
|
||||
/**
|
||||
* @var \Drupal\media\MediaTypeInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer media types']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" media type.
|
||||
$camelids = MediaType::create([
|
||||
'name' => 'Camelids',
|
||||
'id' => 'camelids',
|
||||
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
|
||||
'source' => 'file',
|
||||
]);
|
||||
|
||||
$camelids->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
|
||||
'field_map' => [],
|
||||
'id' => 'camelids',
|
||||
'label' => NULL,
|
||||
'langcode' => 'en',
|
||||
'new_revision' => FALSE,
|
||||
'queue_thumbnail_downloads' => FALSE,
|
||||
'source' => 'file',
|
||||
'source_configuration' => [
|
||||
'source_field' => '',
|
||||
],
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MenuJsonAnonTest extends MenuResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MenuJsonBasicAuthTest extends MenuResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MenuJsonCookieTest extends MenuResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
|
||||
|
||||
use Drupal\system\Entity\Menu;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
abstract class MenuResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'menu';
|
||||
|
||||
/**
|
||||
* @var \Drupal\system\MenuInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer menu']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$menu = Menu::create([
|
||||
'id' => 'menu',
|
||||
'label' => 'Menu',
|
||||
'description' => 'Menu',
|
||||
]);
|
||||
$menu->save();
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'description' => 'Menu',
|
||||
'id' => 'menu',
|
||||
'label' => 'Menu',
|
||||
'langcode' => 'en',
|
||||
'locked' => FALSE,
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return [
|
||||
'user.permissions',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\MenuLinkContent;
|
||||
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
@@ -10,6 +11,8 @@ use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
*/
|
||||
abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -161,9 +164,7 @@ abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'changed' => [
|
||||
[
|
||||
'value' => $this->entity->getChangedTime(),
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
|
||||
+79
-13
@@ -4,15 +4,19 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Node;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node'];
|
||||
public static $modules = ['node', 'path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -23,13 +27,13 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'uid',
|
||||
'revision_timestamp',
|
||||
'revision_uid',
|
||||
'created',
|
||||
'changed',
|
||||
'promote',
|
||||
'sticky',
|
||||
'revision_timestamp',
|
||||
'revision_uid',
|
||||
'path',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -49,6 +53,10 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
$this->grantPermissionsToTestedRole(['access content', 'create camelids content']);
|
||||
break;
|
||||
case 'PATCH':
|
||||
// Do not grant the 'create url aliases' permission to test the case
|
||||
// when the path field is protected/not accessible, see
|
||||
// \Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase
|
||||
// for a positive test.
|
||||
$this->grantPermissionsToTestedRole(['access content', 'edit any camelids content']);
|
||||
break;
|
||||
case 'DELETE':
|
||||
@@ -77,6 +85,7 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
->setCreatedTime(123456789)
|
||||
->setChangedTime(123456789)
|
||||
->setRevisionCreationTime(123456789)
|
||||
->set('path', '/llama')
|
||||
->save();
|
||||
|
||||
return $node;
|
||||
@@ -120,14 +129,10 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => 123456789,
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'changed' => [
|
||||
[
|
||||
'value' => $this->entity->getChangedTime(),
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'promote' => [
|
||||
[
|
||||
@@ -140,9 +145,7 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'revision_timestamp' => [
|
||||
[
|
||||
'value' => 123456789,
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'revision_translation_affected' => [
|
||||
[
|
||||
@@ -171,6 +174,13 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'revision_log' => [],
|
||||
'path' => [
|
||||
[
|
||||
'alias' => '/llama',
|
||||
'pid' => 1,
|
||||
'langcode' => 'en',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -206,4 +216,60 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests PATCHing a node's path with and without 'create url aliases'.
|
||||
*
|
||||
* For a positive test, see the similar test coverage for Term.
|
||||
*
|
||||
* @see \Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase::testPatchPath()
|
||||
*/
|
||||
public function testPatchPath() {
|
||||
$this->initAuthentication();
|
||||
$this->provisionEntityResource();
|
||||
$this->setUpAuthorization('GET');
|
||||
$this->setUpAuthorization('PATCH');
|
||||
|
||||
$url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
|
||||
|
||||
// GET node's current normalization.
|
||||
$response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
|
||||
$normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
|
||||
// @todo In https://www.drupal.org/node/2824851, we will be able to stop
|
||||
// unsetting these fields from the normalization, because
|
||||
// EntityResource::patch() will ignore any fields that are sent that
|
||||
// match the current value (and obviously we're sending the current
|
||||
// value).
|
||||
$normalization = $this->removeFieldsFromNormalization($normalization, [
|
||||
'revision_timestamp',
|
||||
'revision_uid',
|
||||
'created',
|
||||
'changed',
|
||||
'promote',
|
||||
'sticky',
|
||||
]);
|
||||
|
||||
// Change node's path alias.
|
||||
$normalization['path'][0]['alias'] .= 's-rule-the-world';
|
||||
|
||||
// Create node PATCH request.
|
||||
$request_options = [];
|
||||
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// PATCH request: 403 when creating URL aliases unauthorized.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(403, "Access denied on updating field 'path'.", $response);
|
||||
|
||||
// Grant permission to create URL aliases.
|
||||
$this->grantPermissionsToTestedRole(['create url aliases']);
|
||||
|
||||
// Repeat PATCH request: 200.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
$updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
$this->assertSame($normalization['path'], $updated_normalization['path']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ResponsiveImageStyleJsonAnonTest extends ResponsiveImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ResponsiveImageStyleJsonBasicAuthTest extends ResponsiveImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ResponsiveImageStyleJsonCookieTest extends ResponsiveImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
|
||||
|
||||
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for ResponsiveImageStyle entity.
|
||||
*/
|
||||
abstract class ResponsiveImageStyleResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['responsive_image'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'responsive_image_style';
|
||||
|
||||
/**
|
||||
* The ResponsiveImageStyle entity.
|
||||
*
|
||||
* @var \Drupal\responsive_image\ResponsiveImageStyleInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The effect UUID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $effectUuid;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer responsive images']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" responsive image style.
|
||||
$camelids = ResponsiveImageStyle::create([
|
||||
'id' => 'camelids',
|
||||
'label' => 'Camelids',
|
||||
]);
|
||||
$camelids->setBreakpointGroup('test_group');
|
||||
$camelids->setFallbackImageStyle('fallback');
|
||||
$camelids->addImageStyleMapping('test_breakpoint', '1x', [
|
||||
'image_mapping_type' => 'image_style',
|
||||
'image_mapping' => 'small',
|
||||
]);
|
||||
$camelids->addImageStyleMapping('test_breakpoint', '2x', [
|
||||
'image_mapping_type' => 'sizes',
|
||||
'image_mapping' => [
|
||||
'sizes' => '(min-width:700px) 700px, 100vw',
|
||||
'sizes_image_styles' => [
|
||||
'medium' => 'medium',
|
||||
'large' => 'large',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$camelids->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'breakpoint_group' => 'test_group',
|
||||
'dependencies' => [
|
||||
'config' => [
|
||||
'image.style.large',
|
||||
'image.style.medium',
|
||||
],
|
||||
],
|
||||
'fallback_image_style' => 'fallback',
|
||||
'id' => 'camelids',
|
||||
'image_style_mappings' => [
|
||||
0 => [
|
||||
'breakpoint_id' => 'test_breakpoint',
|
||||
'image_mapping' => 'small',
|
||||
'image_mapping_type' => 'image_style',
|
||||
'multiplier' => '1x',
|
||||
],
|
||||
1 => [
|
||||
'breakpoint_id' => 'test_breakpoint',
|
||||
'image_mapping' => [
|
||||
'sizes' => '(min-width:700px) 700px, 100vw',
|
||||
'sizes_image_styles' => [
|
||||
'large' => 'large',
|
||||
'medium' => 'medium',
|
||||
],
|
||||
],
|
||||
'image_mapping_type' => 'sizes',
|
||||
'multiplier' => '2x',
|
||||
],
|
||||
],
|
||||
'label' => 'Camelids',
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer responsive images' permission is required.";
|
||||
}
|
||||
|
||||
}
|
||||
+5
-1
@@ -132,7 +132,11 @@ abstract class ShortcutResourceTestBase extends EntityResourceTestBase {
|
||||
'uri' => 'internal:/',
|
||||
],
|
||||
],
|
||||
'shortcut_set' => 'default',
|
||||
'shortcut_set' => [
|
||||
[
|
||||
'target_id' => 'default',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+70
-6
@@ -4,14 +4,18 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Term;
|
||||
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
abstract class TermResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['taxonomy'];
|
||||
public static $modules = ['taxonomy', 'path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -41,8 +45,12 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
|
||||
case 'POST':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
// Grant the 'create url aliases' permission to test the case when
|
||||
// the path field is accessible, see
|
||||
// \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase
|
||||
// for a negative test.
|
||||
// @todo Update once https://www.drupal.org/node/2824408 lands.
|
||||
$this->grantPermissionsToTestedRole(['administer taxonomy']);
|
||||
$this->grantPermissionsToTestedRole(['administer taxonomy', 'create url aliases']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +72,8 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
|
||||
// Create a "Llama" taxonomy term.
|
||||
$term = Term::create(['vid' => $vocabulary->id()])
|
||||
->setName('Llama')
|
||||
->setChangedTime(123456789);
|
||||
->setChangedTime(123456789)
|
||||
->set('path', '/llama');
|
||||
$term->save();
|
||||
|
||||
return $term;
|
||||
@@ -107,15 +116,20 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'changed' => [
|
||||
[
|
||||
'value' => $this->entity->getChangedTime(),
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'path' => [
|
||||
[
|
||||
'alias' => '/llama',
|
||||
'pid' => 1,
|
||||
'langcode' => 'en',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -134,6 +148,12 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
|
||||
'value' => 'Dramallama',
|
||||
],
|
||||
],
|
||||
'description' => [
|
||||
[
|
||||
'value' => 'Dramallamas are the coolest camelids.',
|
||||
'format' => NULL,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -159,4 +179,48 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests PATCHing a term's path.
|
||||
*
|
||||
* For a negative test, see the similar test coverage for Node.
|
||||
*
|
||||
* @see \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase::testPatchPath()
|
||||
*/
|
||||
public function testPatchPath() {
|
||||
$this->initAuthentication();
|
||||
$this->provisionEntityResource();
|
||||
$this->setUpAuthorization('GET');
|
||||
$this->setUpAuthorization('PATCH');
|
||||
|
||||
$url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
|
||||
|
||||
// GET term's current normalization.
|
||||
$response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
|
||||
$normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
|
||||
// @todo In https://www.drupal.org/node/2824851, we will be able to stop
|
||||
// unsetting these fields from the normalization, because
|
||||
// EntityResource::patch() will ignore any fields that are sent that
|
||||
// match the current value (and obviously we're sending the current
|
||||
// value).
|
||||
$normalization = $this->removeFieldsFromNormalization($normalization, [
|
||||
'changed',
|
||||
]);
|
||||
|
||||
// Change term's path alias.
|
||||
$normalization['path'][0]['alias'] .= 's-rule-the-world';
|
||||
|
||||
// Create term PATCH request.
|
||||
$request_options = [];
|
||||
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// PATCH request: 200.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
$updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
$this->assertSame($normalization['path'], $updated_normalization['path']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class TourJsonAnonTest extends TourResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class TourJsonBasicAuthTest extends TourResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class TourJsonCookieTest extends TourResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\tour\Entity\Tour;
|
||||
|
||||
abstract class TourResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['tour'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'tour';
|
||||
|
||||
/**
|
||||
* @var \Drupal\tour\TourInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['access tour']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$tour = Tour::create([
|
||||
'id' => 'tour-llama',
|
||||
'label' => 'Llama tour',
|
||||
'langcode' => 'en',
|
||||
'module' => 'tour',
|
||||
'routes' => [
|
||||
[
|
||||
'route_name' => '<front>',
|
||||
],
|
||||
],
|
||||
'tips' => [
|
||||
'tour-llama-1' => [
|
||||
'id' => 'tour-llama-1',
|
||||
'plugin' => 'text',
|
||||
'label' => 'Llama',
|
||||
'body' => 'Who handle the awesomeness of llamas?',
|
||||
'weight' => 100,
|
||||
'attributes' => [
|
||||
'data-id' => 'tour-llama-1',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$tour->save();
|
||||
|
||||
return $tour;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'id' => 'tour-llama',
|
||||
'label' => 'Llama tour',
|
||||
'langcode' => 'en',
|
||||
'module' => 'tour',
|
||||
'routes' => [
|
||||
[
|
||||
'route_name' => '<front>',
|
||||
],
|
||||
],
|
||||
'status' => TRUE,
|
||||
'tips' => [
|
||||
'tour-llama-1' => [
|
||||
'id' => 'tour-llama-1',
|
||||
'plugin' => 'text',
|
||||
'label' => 'Llama',
|
||||
'body' => 'Who handle the awesomeness of llamas?',
|
||||
'weight' => 100,
|
||||
'attributes' => [
|
||||
'data-id' => 'tour-llama-1',
|
||||
],
|
||||
],
|
||||
],
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return [
|
||||
'user.permissions',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The following permissions are required: 'access tour' OR 'administer site configuration'.";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,12 +3,15 @@
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\User;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
abstract class UserResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -98,14 +101,10 @@ abstract class UserResourceTestBase extends EntityResourceTestBase {
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => 123456789,
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'changed' => [
|
||||
[
|
||||
'value' => $this->entity->getChangedTime(),
|
||||
],
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
@@ -122,7 +121,7 @@ abstract class UserResourceTestBase extends EntityResourceTestBase {
|
||||
return [
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Dramallama ' . $this->randomMachineName(),
|
||||
'value' => 'Dramallama',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\View;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ViewJsonAnonTest extends ViewResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\View;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ViewJsonBasicAuthTest extends ViewResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\View;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ViewJsonCookieTest extends ViewResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\View;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
abstract class ViewResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'view';
|
||||
|
||||
/**
|
||||
* @var \Drupal\views\ViewEntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer views']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$view = View::create([
|
||||
'id' => 'test_rest',
|
||||
'label' => 'Test REST',
|
||||
]);
|
||||
$view->save();
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'base_field' => 'nid',
|
||||
'base_table' => 'node',
|
||||
'core' => '8.x',
|
||||
'dependencies' => [],
|
||||
'description' => '',
|
||||
'display' => [
|
||||
'default' => [
|
||||
'display_plugin' => 'default',
|
||||
'id' => 'default',
|
||||
'display_title' => 'Master',
|
||||
'position' => 0,
|
||||
'display_options' => [
|
||||
'display_extenders' => [],
|
||||
],
|
||||
'cache_metadata' => [
|
||||
'max-age' => -1,
|
||||
'contexts' => [
|
||||
'languages:language_interface',
|
||||
'url.query_args',
|
||||
],
|
||||
'tags' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
'id' => 'test_rest',
|
||||
'label' => 'Test REST',
|
||||
'langcode' => 'en',
|
||||
'module' => 'views',
|
||||
'status' => TRUE,
|
||||
'tag' => '',
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return [
|
||||
'user.permissions',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\rest\Entity\RestResourceConfig;
|
||||
use Drupal\rest\RestResourceConfigInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
/**
|
||||
* Tests the structure of a REST resource.
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class ResourceTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['hal', 'rest', 'entity_test', 'rest_test'];
|
||||
|
||||
/**
|
||||
* The entity.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create an entity programmatic.
|
||||
$this->entity = EntityTest::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'user_id' => 1,
|
||||
'field_test_text' => [
|
||||
0 => [
|
||||
'value' => $this->randomString(),
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$this->entity->save();
|
||||
|
||||
Role::load(AccountInterface::ANONYMOUS_ROLE)
|
||||
->grantPermission('view test entity')
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a resource without formats cannot be enabled.
|
||||
*/
|
||||
public function testFormats() {
|
||||
RestResourceConfig::create([
|
||||
'id' => 'entity.entity_test',
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => [
|
||||
'GET' => [
|
||||
'supported_auth' => [
|
||||
'basic_auth',
|
||||
],
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Verify that accessing the resource returns 406.
|
||||
$this->drupalGet($this->entity->urlInfo()->setRouteParameter('_format', 'hal_json'));
|
||||
// \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical,
|
||||
// non-REST route a match, but a lower quality one: no format restrictions
|
||||
// means there's always a match and hence when there is no matching REST
|
||||
// route, the non-REST route is used, but can't render into
|
||||
// application/hal+json, so it returns a 406.
|
||||
$this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a resource without authentication cannot be enabled.
|
||||
*/
|
||||
public function testAuthentication() {
|
||||
RestResourceConfig::create([
|
||||
'id' => 'entity.entity_test',
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => [
|
||||
'GET' => [
|
||||
'supported_formats' => [
|
||||
'hal_json',
|
||||
],
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Verify that accessing the resource returns 401.
|
||||
$this->drupalGet($this->entity->urlInfo()->setRouteParameter('_format', 'hal_json'));
|
||||
// \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical,
|
||||
// non-REST route a match, but a lower quality one: no format restrictions
|
||||
// means there's always a match and hence when there is no matching REST
|
||||
// route, the non-REST route is used, but can't render into
|
||||
// application/hal+json, so it returns a 406.
|
||||
$this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that serialization_class is optional.
|
||||
*/
|
||||
public function testSerializationClassIsOptional() {
|
||||
RestResourceConfig::create([
|
||||
'id' => 'serialization_test',
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => [
|
||||
'POST' => [
|
||||
'supported_formats' => [
|
||||
'json',
|
||||
],
|
||||
'supported_auth' => [
|
||||
'cookie',
|
||||
]
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
\Drupal::service('router.builder')->rebuildIfNeeded();
|
||||
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->grantPermission('restful post serialization_test')
|
||||
->save();
|
||||
|
||||
$serialized = $this->container->get('serializer')->serialize(['foo', 'bar'], 'json');
|
||||
$request_options = [
|
||||
RequestOptions::HEADERS => ['Content-Type' => 'application/json'],
|
||||
RequestOptions::BODY => $serialized,
|
||||
];
|
||||
/** @var \GuzzleHttp\ClientInterface $client */
|
||||
$client = $this->getSession()->getDriver()->getClient()->getClient();
|
||||
$response = $client->request('POST', $this->buildUrl('serialization_test', ['query' => ['_format' => 'json']]), $request_options);
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame('["foo","bar"]', (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that resource URI paths are formatted properly.
|
||||
*/
|
||||
public function testUriPaths() {
|
||||
/** @var \Drupal\rest\Plugin\Type\ResourcePluginManager $manager */
|
||||
$manager = \Drupal::service('plugin.manager.rest');
|
||||
|
||||
foreach ($manager->getDefinitions() as $resource => $definition) {
|
||||
foreach ($definition['uri_paths'] as $key => $uri_path) {
|
||||
$this->assertFalse(strpos($uri_path, '//'), 'The resource URI path does not have duplicate slashes.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,6 +62,8 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
* The REST Resource plugin ID can be calculated from this.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @see \Drupal\rest\Entity\RestResourceConfig::__construct()
|
||||
*/
|
||||
protected static $resourceConfigId = NULL;
|
||||
|
||||
@@ -99,6 +101,8 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->serializer = $this->container->get('serializer');
|
||||
|
||||
// Ensure the anonymous user role has no permissions at all.
|
||||
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
|
||||
foreach ($user_role->getPermissions() as $permission) {
|
||||
@@ -172,6 +176,21 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
\Drupal::service('router.builder')->rebuildIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expected error message.
|
||||
*
|
||||
* @param string $method
|
||||
* The HTTP method (GET, POST, PATCH, DELETE).
|
||||
*
|
||||
* @return string
|
||||
* The error string.
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
$resource_plugin_id = str_replace('.', ':', static::$resourceConfigId);
|
||||
$permission = 'restful ' . strtolower($method) . ' ' . $resource_plugin_id;
|
||||
return "The '$permission' permission is required.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the necessary authorization.
|
||||
*
|
||||
@@ -230,29 +249,6 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
*/
|
||||
abstract protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options);
|
||||
|
||||
/**
|
||||
* Return the expected error message.
|
||||
*
|
||||
* @param string $method
|
||||
* The HTTP method (GET, POST, PATCH, DELETE).
|
||||
*
|
||||
* @return string
|
||||
* The error string.
|
||||
*/
|
||||
abstract protected function getExpectedUnauthorizedAccessMessage($method);
|
||||
|
||||
/**
|
||||
* Return the default expected error message if the
|
||||
* bc_entity_resource_permissions is true.
|
||||
*
|
||||
* @param string $method
|
||||
* The HTTP method (GET, POST, PATCH, DELETE).
|
||||
*
|
||||
* @return string
|
||||
* The error string.
|
||||
*/
|
||||
abstract protected function getExpectedBcUnauthorizedAccessMessage($method);
|
||||
|
||||
/**
|
||||
* Initializes authentication.
|
||||
*
|
||||
@@ -321,6 +317,9 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
* 'http_errors = FALSE' request option, nor do we want them to have to
|
||||
* convert Drupal Url objects to strings.
|
||||
*
|
||||
* We also don't want to follow redirects automatically, to ensure these tests
|
||||
* are able to detect when redirects are added or removed.
|
||||
*
|
||||
* @see \GuzzleHttp\ClientInterface::request()
|
||||
*
|
||||
* @param string $method
|
||||
@@ -334,6 +333,7 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
*/
|
||||
protected function request($method, Url $url, array $request_options) {
|
||||
$request_options[RequestOptions::HTTP_ERRORS] = FALSE;
|
||||
$request_options[RequestOptions::ALLOW_REDIRECTS] = FALSE;
|
||||
$request_options = $this->decorateWithXdebugCookie($request_options);
|
||||
$client = $this->getSession()->getDriver()->getClient()->getClient();
|
||||
return $client->request($method, $url->setAbsolute(TRUE)->toString(), $request_options);
|
||||
@@ -351,12 +351,7 @@ abstract class ResourceTestBase extends BrowserTestBase {
|
||||
*/
|
||||
protected function assertResourceResponse($expected_status_code, $expected_body, ResponseInterface $response) {
|
||||
$this->assertSame($expected_status_code, $response->getStatusCode());
|
||||
if ($expected_status_code < 400) {
|
||||
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
|
||||
}
|
||||
else {
|
||||
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
|
||||
}
|
||||
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
|
||||
if ($expected_body !== FALSE) {
|
||||
$this->assertSame($expected_body, (string) $response->getBody());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests that existing sites continue to use permissions for EntityResource.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2664780
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class EntityResourcePermissionsUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['rest', 'serialization'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/drupal-8.rest-rest_update_8203.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rest_update_8203().
|
||||
*/
|
||||
public function testBcEntityResourcePermissionSettingAdded() {
|
||||
$permission_handler = $this->container->get('user.permissions');
|
||||
|
||||
$is_rest_resource_permission = function ($permission) {
|
||||
return $permission['provider'] === 'rest' && (string) $permission['title'] !== 'Administer REST resource configuration';
|
||||
};
|
||||
|
||||
// Make sure we have the expected values before the update.
|
||||
$rest_settings = $this->config('rest.settings');
|
||||
$this->assertFalse(array_key_exists('bc_entity_resource_permissions', $rest_settings->getRawData()));
|
||||
$this->assertEqual([], array_filter($permission_handler->getPermissions(), $is_rest_resource_permission));
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// Make sure we have the expected values after the update.
|
||||
$rest_settings = $this->config('rest.settings');
|
||||
$this->assertTrue(array_key_exists('bc_entity_resource_permissions', $rest_settings->getRawData()));
|
||||
$this->assertTrue($rest_settings->get('bc_entity_resource_permissions'));
|
||||
$rest_permissions = array_keys(array_filter($permission_handler->getPermissions(), $is_rest_resource_permission));
|
||||
$this->assertEqual(['restful delete entity:node', 'restful get entity:node', 'restful patch entity:node', 'restful post entity:node'], $rest_permissions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests method-granularity REST config is simplified to resource-granularity.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2721595
|
||||
* @see rest_post_update_resource_granularity()
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class ResourceGranularityUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['rest', 'serialization'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/drupal-8.rest-rest_post_update_resource_granularity.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rest_post_update_simplify_resource_granularity().
|
||||
*/
|
||||
public function testMethodGranularityConvertedToResourceGranularity() {
|
||||
/** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
|
||||
$resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
|
||||
|
||||
// Make sure we have the expected values before the update.
|
||||
$resource_config_entities = $resource_config_storage->loadMultiple();
|
||||
$this->assertIdentical(['entity.comment', 'entity.node', 'entity.user'], array_keys($resource_config_entities));
|
||||
$this->assertIdentical('method', $resource_config_entities['entity.node']->get('granularity'));
|
||||
$this->assertIdentical('method', $resource_config_entities['entity.comment']->get('granularity'));
|
||||
$this->assertIdentical('method', $resource_config_entities['entity.user']->get('granularity'));
|
||||
|
||||
// Read the existing 'entity:comment' and 'entity:user' resource
|
||||
// configuration so we can verify it after the update.
|
||||
$comment_resource_configuration = $resource_config_entities['entity.comment']->get('configuration');
|
||||
$user_resource_configuration = $resource_config_entities['entity.user']->get('configuration');
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// Make sure we have the expected values after the update.
|
||||
$resource_config_entities = $resource_config_storage->loadMultiple();
|
||||
$this->assertIdentical(['entity.comment', 'entity.node', 'entity.user'], array_keys($resource_config_entities));
|
||||
// 'entity:node' should be updated.
|
||||
$this->assertIdentical('resource', $resource_config_entities['entity.node']->get('granularity'));
|
||||
$this->assertidentical($resource_config_entities['entity.node']->get('configuration'), [
|
||||
'methods' => ['GET', 'POST', 'PATCH', 'DELETE'],
|
||||
'formats' => ['hal_json'],
|
||||
'authentication' => ['basic_auth'],
|
||||
]);
|
||||
// 'entity:comment' should be unchanged.
|
||||
$this->assertIdentical('method', $resource_config_entities['entity.comment']->get('granularity'));
|
||||
$this->assertIdentical($comment_resource_configuration, $resource_config_entities['entity.comment']->get('configuration'));
|
||||
// 'entity:user' should be unchanged.
|
||||
$this->assertIdentical('method', $resource_config_entities['entity.user']->get('granularity'));
|
||||
$this->assertIdentical($user_resource_configuration, $resource_config_entities['entity.user']->get('configuration'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
use Drupal\rest\RestResourceConfigInterface;
|
||||
|
||||
/**
|
||||
* Tests that rest.settings is converted to rest_resource_config entities.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2308745
|
||||
* @see rest_update_8201()
|
||||
* @see rest_post_update_create_rest_resource_config_entities()
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class RestConfigurationEntitiesUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['rest', 'serialization'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/drupal-8.rest-rest_update_8201.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rest_update_8201().
|
||||
*/
|
||||
public function testResourcesConvertedToConfigEntities() {
|
||||
/** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
|
||||
$resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
|
||||
|
||||
// Make sure we have the expected values before the update.
|
||||
$rest_settings = $this->config('rest.settings');
|
||||
$this->assertTrue(array_key_exists('resources', $rest_settings->getRawData()));
|
||||
$this->assertTrue(array_key_exists('entity:node', $rest_settings->getRawData()['resources']));
|
||||
$resource_config_entities = $resource_config_storage->loadMultiple();
|
||||
$this->assertIdentical([], array_keys($resource_config_entities));
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// Make sure we have the expected values after the update.
|
||||
$rest_settings = $this->config('rest.settings');
|
||||
$this->assertFalse(array_key_exists('resources', $rest_settings->getRawData()));
|
||||
$resource_config_entities = $resource_config_storage->loadMultiple();
|
||||
$this->assertIdentical(['entity.node'], array_keys($resource_config_entities));
|
||||
$node_resource_config_entity = $resource_config_entities['entity.node'];
|
||||
$this->assertIdentical(RestResourceConfigInterface::RESOURCE_GRANULARITY, $node_resource_config_entity->get('granularity'));
|
||||
$this->assertIdentical([
|
||||
'methods' => ['GET'],
|
||||
'formats' => ['json'],
|
||||
'authentication' => ['basic_auth'],
|
||||
], $node_resource_config_entity->get('configuration'));
|
||||
$this->assertIdentical(['module' => ['basic_auth', 'node', 'serialization']], $node_resource_config_entity->getDependencies());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Ensures that update hook is run properly for REST Export config.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
class RestExportAuthUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../../tests/fixtures/update/rest-export-with-authentication.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that update hook is run for rest module.
|
||||
*/
|
||||
public function testUpdate() {
|
||||
$this->runUpdates();
|
||||
|
||||
// Get particular view.
|
||||
$view = \Drupal::entityTypeManager()->getStorage('view')->load('rest_export_with_authorization');
|
||||
$displays = $view->get('display');
|
||||
$this->assertIdentical($displays['rest_export_1']['display_options']['auth']['basic_auth'], 'basic_auth', 'Basic authentication is set as authentication method.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Views;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the display of an excluded field that is used as a token.
|
||||
*
|
||||
* @group rest
|
||||
* @see \Drupal\rest\Plugin\views\display\RestExport
|
||||
* @see \Drupal\rest\Plugin\views\row\DataFieldRow
|
||||
*/
|
||||
class ExcludedFieldTokenTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\views\ViewExecutable
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* The views that are used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_excluded_field_token_display'];
|
||||
|
||||
/**
|
||||
* The modules that need to be installed for this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity_test',
|
||||
'rest_test_views',
|
||||
'node',
|
||||
'field',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['rest_test_views']);
|
||||
|
||||
// Create some test content.
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
Node::create([
|
||||
'type' => 'article',
|
||||
'title' => 'Article test ' . $i,
|
||||
])->save();
|
||||
}
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
$this->view = Views::getView('test_excluded_field_token_display');
|
||||
$this->view->setDisplay('rest_export_1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the display of an excluded title field when used as a token.
|
||||
*/
|
||||
public function testExcludedTitleTokenDisplay() {
|
||||
$actual_json = $this->drupalGet($this->view->getPath(), ['query' => ['_format' => 'json']]);
|
||||
$this->assertResponse(200);
|
||||
|
||||
$expected = [
|
||||
['nothing' => 'Article test 10'],
|
||||
['nothing' => 'Article test 9'],
|
||||
['nothing' => 'Article test 8'],
|
||||
['nothing' => 'Article test 7'],
|
||||
['nothing' => 'Article test 6'],
|
||||
['nothing' => 'Article test 5'],
|
||||
['nothing' => 'Article test 4'],
|
||||
['nothing' => 'Article test 3'],
|
||||
['nothing' => 'Article test 2'],
|
||||
['nothing' => 'Article test 1'],
|
||||
];
|
||||
$this->assertIdentical($actual_json, json_encode($expected));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests authentication for REST display.
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class RestExportAuthTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['rest', 'views_ui', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer views']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that correct authentication providers are available for choosing.
|
||||
*
|
||||
* @link https://www.drupal.org/node/2825204
|
||||
*/
|
||||
public function testAuthProvidersOptions() {
|
||||
$view_id = 'test_view_rest_export';
|
||||
$view_label = 'Test view (REST export)';
|
||||
$view_display = 'rest_export_1';
|
||||
$view_rest_path = 'test-view/rest-export';
|
||||
|
||||
// Create new view.
|
||||
$this->drupalPostForm('admin/structure/views/add', [
|
||||
'id' => $view_id,
|
||||
'label' => $view_label,
|
||||
'show[wizard_key]' => 'users',
|
||||
'rest_export[path]' => $view_rest_path,
|
||||
'rest_export[create]' => TRUE,
|
||||
], t('Save and edit'));
|
||||
|
||||
$this->drupalGet("admin/structure/views/nojs/display/$view_id/$view_display/auth");
|
||||
// The "basic_auth" will always be available since module,
|
||||
// providing it, has the same name.
|
||||
$this->assertField('edit-auth-basic-auth', 'Basic auth is available for choosing.');
|
||||
// The "cookie" authentication provider defined by "user" module.
|
||||
$this->assertField('edit-auth-cookie', 'Cookie-based auth can be chosen.');
|
||||
// Wrong behavior in "getAuthOptions()" method makes this option available
|
||||
// instead of "cookie".
|
||||
// @see \Drupal\rest\Plugin\views\display\RestExport::getAuthOptions()
|
||||
$this->assertNoField('edit-auth-user', 'Wrong authentication option is unavailable.');
|
||||
|
||||
$this->drupalPostForm(NULL, ['auth[basic_auth]' => 1, 'auth[cookie]' => 1], 'Apply');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
|
||||
$view = View::load($view_id);
|
||||
$this->assertEquals(['basic_auth', 'cookie'], $view->getDisplay('rest_export_1')['display_options']['auth']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,832 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\Views;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests the serializer style plugin.
|
||||
*
|
||||
* @group rest
|
||||
* @see \Drupal\rest\Plugin\views\display\RestExport
|
||||
* @see \Drupal\rest\Plugin\views\style\Serializer
|
||||
* @see \Drupal\rest\Plugin\views\row\DataEntityRow
|
||||
* @see \Drupal\rest\Plugin\views\row\DataFieldRow
|
||||
*/
|
||||
class StyleSerializerTest extends ViewTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui', 'entity_test', 'hal', 'rest_test_views', 'node', 'text', 'field', 'language', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_serializer_display_field', 'test_serializer_display_entity', 'test_serializer_display_entity_translated', 'test_serializer_node_display_field', 'test_serializer_node_exposed_filter'];
|
||||
|
||||
/**
|
||||
* A user with administrative privileges to look at test entity and configure views.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['rest_test_views']);
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer views', 'administer entity_test content', 'access user profiles', 'view test entity']);
|
||||
|
||||
// Save some entity_test entities.
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
EntityTest::create(['name' => 'test_' . $i, 'user_id' => $this->adminUser->id()])->save();
|
||||
}
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the auth options restricts access to a REST views display.
|
||||
*/
|
||||
public function testRestViewsAuthentication() {
|
||||
// Assume the view is hidden behind a permission.
|
||||
$this->drupalGet('test/serialize/auth_with_perm', ['query' => ['_format' => 'json']]);
|
||||
$this->assertResponse(401);
|
||||
|
||||
// Not even logging in would make it possible to see the view, because then
|
||||
// we are denied based on authentication method (cookie).
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('test/serialize/auth_with_perm', ['query' => ['_format' => 'json']]);
|
||||
$this->assertResponse(403);
|
||||
$this->drupalLogout();
|
||||
|
||||
// But if we use the basic auth authentication strategy, we should be able
|
||||
// to see the page.
|
||||
$url = $this->buildUrl('test/serialize/auth_with_perm');
|
||||
$response = \Drupal::httpClient()->get($url, [
|
||||
'auth' => [$this->adminUser->getUsername(), $this->adminUser->pass_raw],
|
||||
]);
|
||||
|
||||
// Ensure that any changes to variables in the other thread are picked up.
|
||||
$this->refreshVariables();
|
||||
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the behavior of the Serializer callback paths and row plugins.
|
||||
*/
|
||||
public function testSerializerResponses() {
|
||||
// Test the serialize callback.
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->initDisplay();
|
||||
$this->executeView($view);
|
||||
|
||||
$actual_json = $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
|
||||
$this->assertResponse(200);
|
||||
$this->assertCacheTags($view->getCacheTags());
|
||||
$this->assertCacheContexts(['languages:language_interface', 'theme', 'request_format']);
|
||||
// @todo Due to https://www.drupal.org/node/2352009 we can't yet test the
|
||||
// propagation of cache max-age.
|
||||
|
||||
// Test the http Content-type.
|
||||
$headers = $this->drupalGetHeaders();
|
||||
$this->assertSame(['application/json'], $headers['Content-Type']);
|
||||
|
||||
$expected = [];
|
||||
foreach ($view->result as $row) {
|
||||
$expected_row = [];
|
||||
foreach ($view->field as $id => $field) {
|
||||
$expected_row[$id] = $field->render($row);
|
||||
}
|
||||
$expected[] = $expected_row;
|
||||
}
|
||||
|
||||
$this->assertIdentical($actual_json, json_encode($expected), 'The expected JSON output was found.');
|
||||
|
||||
|
||||
// Test that the rendered output and the preview output are the same.
|
||||
$view->destroy();
|
||||
$view->setDisplay('rest_export_1');
|
||||
// Mock the request content type by setting it on the display handler.
|
||||
$view->display_handler->setContentType('json');
|
||||
$output = $view->preview();
|
||||
$this->assertIdentical($actual_json, (string) drupal_render_root($output), 'The expected JSON preview output was found.');
|
||||
|
||||
// Test a 403 callback.
|
||||
$this->drupalGet('test/serialize/denied');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Test the entity rows.
|
||||
$view = Views::getView('test_serializer_display_entity');
|
||||
$view->initDisplay();
|
||||
$this->executeView($view);
|
||||
|
||||
// Get the serializer service.
|
||||
$serializer = $this->container->get('serializer');
|
||||
|
||||
$entities = [];
|
||||
foreach ($view->result as $row) {
|
||||
$entities[] = $row->_entity;
|
||||
}
|
||||
|
||||
$expected = $serializer->serialize($entities, 'json');
|
||||
|
||||
$actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
|
||||
$this->assertResponse(200);
|
||||
$this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
|
||||
$expected_cache_tags = $view->getCacheTags();
|
||||
$expected_cache_tags[] = 'entity_test_list';
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
foreach ($entities as $entity) {
|
||||
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, $entity->getCacheTags());
|
||||
}
|
||||
$this->assertCacheTags($expected_cache_tags);
|
||||
$this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
|
||||
|
||||
$expected = $serializer->serialize($entities, 'hal_json');
|
||||
$actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'hal_json']]);
|
||||
$this->assertIdentical($actual_json, $expected, 'The expected HAL output was found.');
|
||||
$this->assertCacheTags($expected_cache_tags);
|
||||
|
||||
// Change the default format to xml.
|
||||
$view->setDisplay('rest_export_1');
|
||||
$view->getDisplay()->setOption('style', [
|
||||
'type' => 'serializer',
|
||||
'options' => [
|
||||
'uses_fields' => FALSE,
|
||||
'formats' => [
|
||||
'xml' => 'xml',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
$expected = $serializer->serialize($entities, 'xml');
|
||||
$actual_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
|
||||
$this->assertSame(trim($expected), $actual_xml);
|
||||
$this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
|
||||
|
||||
// Allow multiple formats.
|
||||
$view->setDisplay('rest_export_1');
|
||||
$view->getDisplay()->setOption('style', [
|
||||
'type' => 'serializer',
|
||||
'options' => [
|
||||
'uses_fields' => FALSE,
|
||||
'formats' => [
|
||||
'xml' => 'xml',
|
||||
'json' => 'json',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
$expected = $serializer->serialize($entities, 'json');
|
||||
$actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
|
||||
$this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
|
||||
$expected = $serializer->serialize($entities, 'xml');
|
||||
$actual_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
|
||||
$this->assertSame(trim($expected), $actual_xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies site maintenance mode functionality.
|
||||
*/
|
||||
public function testSiteMaintenance() {
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->initDisplay();
|
||||
$this->executeView($view);
|
||||
|
||||
// Set the site to maintenance mode.
|
||||
$this->container->get('state')->set('system.maintenance_mode', TRUE);
|
||||
|
||||
$this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
|
||||
// Verify that the endpoint is unavailable for anonymous users.
|
||||
$this->assertResponse(503);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a request on the request stack with a specified format.
|
||||
*
|
||||
* @param string $format
|
||||
* The new request format.
|
||||
*/
|
||||
protected function addRequestWithFormat($format) {
|
||||
$request = \Drupal::request();
|
||||
$request = clone $request;
|
||||
$request->setRequestFormat($format);
|
||||
|
||||
\Drupal::requestStack()->push($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests REST export with views render caching enabled.
|
||||
*/
|
||||
public function testRestRenderCaching() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
/** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */
|
||||
$render_cache = \Drupal::service('render_cache');
|
||||
|
||||
// Enable render caching for the views.
|
||||
/** @var \Drupal\views\ViewEntityInterface $storage */
|
||||
$storage = View::load('test_serializer_display_entity');
|
||||
$options = &$storage->getDisplay('default');
|
||||
$options['display_options']['cache'] = [
|
||||
'type' => 'tag',
|
||||
];
|
||||
$storage->save();
|
||||
|
||||
$original = DisplayPluginBase::buildBasicRenderable('test_serializer_display_entity', 'rest_export_1');
|
||||
|
||||
// Ensure that there is no corresponding render cache item yet.
|
||||
$original['#cache'] += ['contexts' => []];
|
||||
$original['#cache']['contexts'] = Cache::mergeContexts($original['#cache']['contexts'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
|
||||
|
||||
$cache_tags = [
|
||||
'config:views.view.test_serializer_display_entity',
|
||||
'entity_test:1',
|
||||
'entity_test:10',
|
||||
'entity_test:2',
|
||||
'entity_test:3',
|
||||
'entity_test:4',
|
||||
'entity_test:5',
|
||||
'entity_test:6',
|
||||
'entity_test:7',
|
||||
'entity_test:8',
|
||||
'entity_test:9',
|
||||
'entity_test_list'
|
||||
];
|
||||
$cache_contexts = [
|
||||
'entity_test_view_grants',
|
||||
'languages:language_interface',
|
||||
'theme',
|
||||
'request_format',
|
||||
];
|
||||
|
||||
$this->assertFalse($render_cache->get($original));
|
||||
|
||||
// Request the page, once in XML and once in JSON to ensure that the caching
|
||||
// varies by it.
|
||||
$result1 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
|
||||
$this->addRequestWithFormat('json');
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertCacheContexts($cache_contexts);
|
||||
$this->assertCacheTags($cache_tags);
|
||||
$this->assertTrue($render_cache->get($original));
|
||||
|
||||
$result_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
|
||||
$this->addRequestWithFormat('xml');
|
||||
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
|
||||
$this->assertCacheContexts($cache_contexts);
|
||||
$this->assertCacheTags($cache_tags);
|
||||
$this->assertTrue($render_cache->get($original));
|
||||
|
||||
// Ensure that the XML output is different from the JSON one.
|
||||
$this->assertNotEqual($result1, $result_xml);
|
||||
|
||||
// Ensure that the cached page works.
|
||||
$result2 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
|
||||
$this->addRequestWithFormat('json');
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertEqual($result2, $result1);
|
||||
$this->assertCacheContexts($cache_contexts);
|
||||
$this->assertCacheTags($cache_tags);
|
||||
$this->assertTrue($render_cache->get($original));
|
||||
|
||||
// Create a new entity and ensure that the cache tags are taken over.
|
||||
EntityTest::create(['name' => 'test_11', 'user_id' => $this->adminUser->id()])->save();
|
||||
$result3 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
|
||||
$this->addRequestWithFormat('json');
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertNotEqual($result3, $result2);
|
||||
|
||||
// Add the new entity cache tag and remove the first one, because we just
|
||||
// show 10 items in total.
|
||||
$cache_tags[] = 'entity_test:11';
|
||||
unset($cache_tags[array_search('entity_test:1', $cache_tags)]);
|
||||
|
||||
$this->assertCacheContexts($cache_contexts);
|
||||
$this->assertCacheTags($cache_tags);
|
||||
$this->assertTrue($render_cache->get($original));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the response format configuration.
|
||||
*/
|
||||
public function testResponseFormatConfiguration() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$style_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/style_options';
|
||||
|
||||
// Select only 'xml' as an accepted format.
|
||||
$this->drupalPostForm($style_options, ['style_options[formats][xml]' => 'xml'], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
// Should return a 406.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(406, 'A 406 response was returned when JSON was requested.');
|
||||
// Should return a 200.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
|
||||
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
|
||||
$this->assertResponse(200, 'A 200 response was returned when XML was requested.');
|
||||
|
||||
// Add 'json' as an accepted format, so we have multiple.
|
||||
$this->drupalPostForm($style_options, ['style_options[formats][json]' => 'json'], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
// Should return a 200.
|
||||
// @todo This should be fixed when we have better content negotiation.
|
||||
$this->drupalGet('test/serialize/field');
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when any format was requested.');
|
||||
|
||||
// Should return a 200. Emulates a sample Firefox header.
|
||||
$this->drupalGet('test/serialize/field', [], ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']);
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when a browser accept header was requested.');
|
||||
|
||||
// Should return a 200.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
|
||||
$headers = $this->drupalGetHeaders();
|
||||
$this->assertEqual($headers['Content-Type'], ['application/json'], 'The header Content-type is correct.');
|
||||
// Should return a 200.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
|
||||
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
|
||||
$this->assertResponse(200, 'A 200 response was returned when XML was requested');
|
||||
$headers = $this->drupalGetHeaders();
|
||||
$this->assertSame(['text/xml; charset=UTF-8'], $headers['Content-Type']);
|
||||
// Should return a 406.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'html']]);
|
||||
// We want to show the first format by default, see
|
||||
// \Drupal\rest\Plugin\views\style\Serializer::render.
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
|
||||
|
||||
// Now configure now format, so all of them should be allowed.
|
||||
$this->drupalPostForm($style_options, ['style_options[formats][json]' => '0', 'style_options[formats][xml]' => '0'], t('Apply'));
|
||||
|
||||
// Should return a 200.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
|
||||
// Should return a 200.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
|
||||
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
|
||||
$this->assertResponse(200, 'A 200 response was returned when XML was requested');
|
||||
// Should return a 200.
|
||||
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'html']]);
|
||||
// We want to show the first format by default, see
|
||||
// \Drupal\rest\Plugin\views\style\Serializer::render.
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the field ID alias functionality of the DataFieldRow plugin.
|
||||
*/
|
||||
public function testUIFieldAlias() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Test the UI settings for adding field ID aliases.
|
||||
$this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1');
|
||||
$row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options';
|
||||
$this->assertLinkByHref($row_options);
|
||||
|
||||
// Test an empty string for an alias, this should not be used. This also
|
||||
// tests that the form can be submitted with no aliases.
|
||||
$this->drupalPostForm($row_options, ['row_options[field_options][name][alias]' => ''], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
$expected = [];
|
||||
foreach ($view->result as $row) {
|
||||
$expected_row = [];
|
||||
foreach ($view->field as $id => $field) {
|
||||
$expected_row[$id] = $field->render($row);
|
||||
}
|
||||
$expected[] = $expected_row;
|
||||
}
|
||||
|
||||
$this->assertIdentical(Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])), $this->castSafeStrings($expected));
|
||||
|
||||
// Test a random aliases for fields, they should be replaced.
|
||||
$alias_map = [
|
||||
'name' => $this->randomMachineName(),
|
||||
// Use # to produce an invalid character for the validation.
|
||||
'nothing' => '#' . $this->randomMachineName(),
|
||||
'created' => 'created',
|
||||
];
|
||||
|
||||
$edit = ['row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']];
|
||||
$this->drupalPostForm($row_options, $edit, t('Apply'));
|
||||
$this->assertText(t('The machine-readable name must contain only letters, numbers, dashes and underscores.'));
|
||||
|
||||
// Change the map alias value to a valid one.
|
||||
$alias_map['nothing'] = $this->randomMachineName();
|
||||
|
||||
$edit = ['row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']];
|
||||
$this->drupalPostForm($row_options, $edit, t('Apply'));
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
$expected = [];
|
||||
foreach ($view->result as $row) {
|
||||
$expected_row = [];
|
||||
foreach ($view->field as $id => $field) {
|
||||
$expected_row[$alias_map[$id]] = $field->render($row);
|
||||
}
|
||||
$expected[] = $expected_row;
|
||||
}
|
||||
|
||||
$this->assertIdentical(Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])), $this->castSafeStrings($expected));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the raw output options for row field rendering.
|
||||
*/
|
||||
public function testFieldRawOutput() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Test the UI settings for adding field ID aliases.
|
||||
$this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1');
|
||||
$row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options';
|
||||
$this->assertLinkByHref($row_options);
|
||||
|
||||
// Test an empty string for an alias, this should not be used. This also
|
||||
// tests that the form can be submitted with no aliases.
|
||||
$values = [
|
||||
'row_options[field_options][created][raw_output]' => '1',
|
||||
'row_options[field_options][name][raw_output]' => '1',
|
||||
];
|
||||
$this->drupalPostForm($row_options, $values, t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('entity_test');
|
||||
|
||||
// Update the name for each to include a script tag.
|
||||
foreach ($storage->loadMultiple() as $entity_test) {
|
||||
$name = $entity_test->name->value;
|
||||
$entity_test->set('name', "<script>$name</script>");
|
||||
$entity_test->save();
|
||||
}
|
||||
|
||||
// Just test the raw 'created' value against each row.
|
||||
foreach (Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])) as $index => $values) {
|
||||
$this->assertIdentical($values['created'], $view->result[$index]->views_test_data_created, 'Expected raw created value found.');
|
||||
$this->assertIdentical($values['name'], $view->result[$index]->views_test_data_name, 'Expected raw name value found.');
|
||||
}
|
||||
|
||||
// Test result with an excluded field.
|
||||
$view->setDisplay('rest_export_1');
|
||||
$view->displayHandlers->get('rest_export_1')->overrideOption('fields', [
|
||||
'name' => [
|
||||
'id' => 'name',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
],
|
||||
'created' => [
|
||||
'id' => 'created',
|
||||
'exclude' => TRUE,
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'created',
|
||||
'relationship' => 'none',
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
$this->executeView($view);
|
||||
foreach (Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])) as $index => $values) {
|
||||
$this->assertTrue(!isset($values['created']), 'Excluded value not found.');
|
||||
}
|
||||
// Test that the excluded field is not shown in the row options.
|
||||
$this->drupalGet('admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options');
|
||||
$this->assertNoText('created');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the live preview output for json output.
|
||||
*/
|
||||
public function testLivePreview() {
|
||||
// We set up a request so it looks like an request in the live preview.
|
||||
$request = new Request();
|
||||
$request->query->add([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']);
|
||||
/** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
|
||||
$request_stack = \Drupal::service('request_stack');
|
||||
$request_stack->push($request);
|
||||
|
||||
$view = Views::getView('test_serializer_display_entity');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
// Get the serializer service.
|
||||
$serializer = $this->container->get('serializer');
|
||||
|
||||
$entities = [];
|
||||
foreach ($view->result as $row) {
|
||||
$entities[] = $row->_entity;
|
||||
}
|
||||
|
||||
$expected = $serializer->serialize($entities, 'json');
|
||||
|
||||
$view->live_preview = TRUE;
|
||||
|
||||
$build = $view->preview();
|
||||
$rendered_json = $build['#plain_text'];
|
||||
$this->assertTrue(!isset($build['#markup']) && $rendered_json == $expected, 'Ensure the previewed json is escaped.');
|
||||
$view->destroy();
|
||||
|
||||
$expected = $serializer->serialize($entities, 'xml');
|
||||
|
||||
// Change the request format to xml.
|
||||
$view->setDisplay('rest_export_1');
|
||||
$view->getDisplay()->setOption('style', [
|
||||
'type' => 'serializer',
|
||||
'options' => [
|
||||
'uses_fields' => FALSE,
|
||||
'formats' => [
|
||||
'xml' => 'xml',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->executeView($view);
|
||||
$build = $view->preview();
|
||||
$rendered_xml = $build['#plain_text'];
|
||||
$this->assertEqual($rendered_xml, $expected, 'Ensure we preview xml when we change the request format.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the views interface for REST export displays.
|
||||
*/
|
||||
public function testSerializerViewsUI() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
// Click the "Update preview button".
|
||||
$this->drupalPostForm('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1', $edit = [], t('Update preview'));
|
||||
$this->assertResponse(200);
|
||||
// Check if we receive the expected result.
|
||||
$result = $this->xpath('//div[@id="views-live-preview"]/pre');
|
||||
$json_preview = $result[0]->getText();
|
||||
$this->assertSame($json_preview, $this->drupalGet('test/serialize/field'), 'The expected JSON preview output was found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the field row style using fieldapi fields.
|
||||
*/
|
||||
public function testFieldapiField() {
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
|
||||
$this->assertEqual($result[0]['nid'], $node->id());
|
||||
$this->assertEqual($result[0]['body'], $node->body->processed);
|
||||
|
||||
// Make sure that serialized fields are not exposed to XSS.
|
||||
$node = $this->drupalCreateNode();
|
||||
$node->body = [
|
||||
'value' => '<script type="text/javascript">alert("node-body");</script>' . $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
];
|
||||
$node->save();
|
||||
$result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
|
||||
$this->assertEqual($result[1]['nid'], $node->id());
|
||||
$this->assertTrue(strpos($this->getRawContent(), "<script") === FALSE, "No script tag is present in the raw page contents.");
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Add an alias and make the output raw.
|
||||
$row_options = 'admin/structure/views/nojs/display/test_serializer_node_display_field/rest_export_1/row_options';
|
||||
|
||||
// Test an empty string for an alias, this should not be used. This also
|
||||
// tests that the form can be submitted with no aliases.
|
||||
$this->drupalPostForm($row_options, ['row_options[field_options][title][raw_output]' => '1'], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
$view = Views::getView('test_serializer_node_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
// Test the raw 'created' value against each row.
|
||||
foreach (Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']])) as $index => $values) {
|
||||
$this->assertIdentical($values['title'], $view->result[$index]->_entity->title->value, 'Expected raw title value found.');
|
||||
}
|
||||
|
||||
// Test that multiple raw body fields are shown.
|
||||
// Make the body field unlimited cardinatlity.
|
||||
$storage_definition = $node->getFieldDefinition('body')->getFieldStorageDefinition();
|
||||
$storage_definition->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$storage_definition->save();
|
||||
|
||||
$this->drupalPostForm($row_options, ['row_options[field_options][body][raw_output]' => '1'], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$body = [
|
||||
'value' => '<script type="text/javascript">alert("node-body");</script>' . $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
];
|
||||
// Add two body items.
|
||||
$node->body = [$body, $body];
|
||||
$node->save();
|
||||
|
||||
$view = Views::getView('test_serializer_node_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
$result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
|
||||
$this->assertEqual(count($result[2]['body']), $node->body->count(), 'Expected count of values');
|
||||
$this->assertEqual($result[2]['body'], array_map(function ($item) {
|
||||
return $item['value'];
|
||||
}, $node->body->getValue()), 'Expected raw body values found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the "Grouped rows" functionality.
|
||||
*/
|
||||
public function testGroupRows() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
// Create a text field with cardinality set to unlimited.
|
||||
$field_name = 'field_group_rows';
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
]);
|
||||
$field_storage->save();
|
||||
// Create an instance of the text field on the content type.
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
]);
|
||||
$field->save();
|
||||
$grouped_field_values = ['a', 'b', 'c'];
|
||||
$edit = [
|
||||
'title' => $this->randomMachineName(),
|
||||
$field_name => $grouped_field_values,
|
||||
];
|
||||
$this->drupalCreateNode($edit);
|
||||
$view = Views::getView('test_serializer_node_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
// Override the view's fields to include the field_group_rows field, set the
|
||||
// group_rows setting to true.
|
||||
$fields = [
|
||||
$field_name => [
|
||||
'id' => $field_name,
|
||||
'table' => 'node__' . $field_name,
|
||||
'field' => $field_name,
|
||||
'type' => 'string',
|
||||
'group_rows' => TRUE,
|
||||
],
|
||||
];
|
||||
$view->displayHandlers->get('default')->overrideOption('fields', $fields);
|
||||
$build = $view->preview();
|
||||
// Get the serializer service.
|
||||
$serializer = $this->container->get('serializer');
|
||||
// Check if the field_group_rows field is grouped.
|
||||
$expected = [];
|
||||
$expected[] = [$field_name => implode(', ', $grouped_field_values)];
|
||||
$this->assertEqual($serializer->serialize($expected, 'json'), (string) $renderer->renderRoot($build));
|
||||
// Set the group rows setting to false.
|
||||
$view = Views::getView('test_serializer_node_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$fields[$field_name]['group_rows'] = FALSE;
|
||||
$view->displayHandlers->get('default')->overrideOption('fields', $fields);
|
||||
$build = $view->preview();
|
||||
// Check if the field_group_rows field is ungrouped and displayed per row.
|
||||
$expected = [];
|
||||
foreach ($grouped_field_values as $grouped_field_value) {
|
||||
$expected[] = [$field_name => $grouped_field_value];
|
||||
}
|
||||
$this->assertEqual($serializer->serialize($expected, 'json'), (string) $renderer->renderRoot($build));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the exposed filter works.
|
||||
*
|
||||
* There is an exposed filter on the title field which takes a title query
|
||||
* parameter. This is set to filter nodes by those whose title starts with
|
||||
* the value provided.
|
||||
*/
|
||||
public function testRestViewExposedFilter() {
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$node0 = $this->drupalCreateNode(['title' => 'Node 1']);
|
||||
$node1 = $this->drupalCreateNode(['title' => 'Node 11']);
|
||||
$node2 = $this->drupalCreateNode(['title' => 'Node 111']);
|
||||
|
||||
// Test that no filter brings back all three nodes.
|
||||
$result = Json::decode($this->drupalGet('test/serialize/node-exposed-filter', ['query' => ['_format' => 'json']]));
|
||||
|
||||
$expected = [
|
||||
0 => [
|
||||
'nid' => $node0->id(),
|
||||
'body' => $node0->body->processed,
|
||||
],
|
||||
1 => [
|
||||
'nid' => $node1->id(),
|
||||
'body' => $node1->body->processed,
|
||||
],
|
||||
2 => [
|
||||
'nid' => $node2->id(),
|
||||
'body' => $node2->body->processed,
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEqual($result, $expected, 'Querying a view with no exposed filter returns all nodes.');
|
||||
|
||||
// Test that title starts with 'Node 11' query finds 2 of the 3 nodes.
|
||||
$result = Json::decode($this->drupalGet('test/serialize/node-exposed-filter', ['query' => ['_format' => 'json', 'title' => 'Node 11']]));
|
||||
|
||||
$expected = [
|
||||
0 => [
|
||||
'nid' => $node1->id(),
|
||||
'body' => $node1->body->processed,
|
||||
],
|
||||
1 => [
|
||||
'nid' => $node2->id(),
|
||||
'body' => $node2->body->processed,
|
||||
],
|
||||
];
|
||||
|
||||
$cache_contexts = [
|
||||
'languages:language_content',
|
||||
'languages:language_interface',
|
||||
'theme',
|
||||
'request_format',
|
||||
'user.node_grants:view',
|
||||
'url',
|
||||
];
|
||||
|
||||
$this->assertEqual($result, $expected, 'Querying a view with a starts with exposed filter on the title returns nodes whose title starts with value provided.');
|
||||
$this->assertCacheContexts($cache_contexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multilingual entity rows.
|
||||
*/
|
||||
public function testMulEntityRows() {
|
||||
// Create some languages.
|
||||
ConfigurableLanguage::createFromLangcode('l1')->save();
|
||||
ConfigurableLanguage::createFromLangcode('l2')->save();
|
||||
|
||||
// Create an entity with no translations.
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_mul');
|
||||
$storage->create(['langcode' => 'l1', 'name' => 'mul-none'])->save();
|
||||
|
||||
// Create some entities with translations.
|
||||
$entity = $storage->create(['langcode' => 'l1', 'name' => 'mul-l1-orig']);
|
||||
$entity->save();
|
||||
$entity->addTranslation('l2', ['name' => 'mul-l1-l2'])->save();
|
||||
$entity = $storage->create(['langcode' => 'l2', 'name' => 'mul-l2-orig']);
|
||||
$entity->save();
|
||||
$entity->addTranslation('l1', ['name' => 'mul-l2-l1'])->save();
|
||||
|
||||
// Get the names of the output.
|
||||
$json = $this->drupalGet('test/serialize/translated_entity', ['query' => ['_format' => 'json']]);
|
||||
$decoded = $this->container->get('serializer')->decode($json, 'hal_json');
|
||||
$names = [];
|
||||
foreach ($decoded as $item) {
|
||||
$names[] = $item['name'][0]['value'];
|
||||
}
|
||||
sort($names);
|
||||
|
||||
// Check that the names are correct.
|
||||
$expected = ['mul-l1-l2', 'mul-l1-orig', 'mul-l2-l1', 'mul-l2-orig', 'mul-none'];
|
||||
$this->assertIdentical($names, $expected, 'The translated content was found in the JSON.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,9 +30,9 @@ class ConfigDependenciesTest extends KernelTestBase {
|
||||
$rest_config = RestResourceConfig::create($configuration);
|
||||
|
||||
$result = $config_dependencies->calculateDependencies($rest_config);
|
||||
$this->assertEquals(['module' => [
|
||||
'basic_auth', 'serialization', 'hal',
|
||||
]], $result);
|
||||
$this->assertEquals([
|
||||
'module' => ['basic_auth', 'serialization', 'hal'],
|
||||
], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,7 @@ class RequestHandlerTest extends KernelTestBase {
|
||||
*/
|
||||
public function testHandle() {
|
||||
$request = new Request();
|
||||
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_format' => 'json']));
|
||||
$route_match = new RouteMatch('test', (new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_format' => 'json']))->setMethods(['GET']));
|
||||
|
||||
$resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
|
||||
$resource->get(NULL, $request)
|
||||
@@ -76,7 +76,7 @@ class RequestHandlerTest extends KernelTestBase {
|
||||
$this->assertEquals($response, $handler_response);
|
||||
|
||||
// We will call the patch method this time.
|
||||
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_content_type_format' => 'json']));
|
||||
$route_match = new RouteMatch('test', (new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_content_type_format' => 'json']))->setMethods(['PATCH']));
|
||||
$request->setMethod('PATCH');
|
||||
$response = new ResourceResponse([]);
|
||||
$resource->patch(NULL, $request)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Kernel\Views;
|
||||
|
||||
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the auth option of rest exports.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\rest\Plugin\views\display\RestExport
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class RestExportAuthTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'rest',
|
||||
'views_ui',
|
||||
'basic_auth',
|
||||
'serialization',
|
||||
'rest',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $testViews = ['rest_export_with_authorization_correction'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installConfig(['user']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that rest export auth settings are automatically corrected.
|
||||
*
|
||||
* @see rest_update_8401()
|
||||
* @see rest_views_presave()
|
||||
* @see \Drupal\rest\Tests\Update\RestExportAuthCorrectionUpdateTest
|
||||
*/
|
||||
public function testAuthCorrection() {
|
||||
// Get particular view.
|
||||
$view = \Drupal::entityTypeManager()
|
||||
->getStorage('view')
|
||||
->load('rest_export_with_authorization_correction');
|
||||
$displays = $view->get('display');
|
||||
$this->assertSame($displays['rest_export_1']['display_options']['auth'], ['cookie'], 'Cookie is used for authentication');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -141,8 +141,8 @@ class CollectRoutesTest extends UnitTestCase {
|
||||
$requirements_1 = $this->routes->get('test_1')->getRequirements();
|
||||
$requirements_2 = $this->routes->get('view.test_view.page_1')->getRequirements();
|
||||
|
||||
$this->assertEquals(count($requirements_1), 0, 'First route has no requirement.');
|
||||
$this->assertEquals(count($requirements_2), 2, 'Views route with rest export had the format and method requirements added.');
|
||||
$this->assertEquals(0, count($requirements_1), 'First route has no requirement.');
|
||||
$this->assertEquals(1, count($requirements_2), 'Views route with rest export had the format requirement added.');
|
||||
|
||||
// Check auth options.
|
||||
$auth = $this->routes->get('view.test_view.page_1')->getOption('_auth');
|
||||
|
||||
+21
-21
@@ -79,11 +79,6 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
|
||||
* @dataProvider providerTestResponseFormat
|
||||
*/
|
||||
public function testResponseFormat($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) {
|
||||
$parameters = [];
|
||||
if ($request_format !== FALSE) {
|
||||
$parameters['_format'] = $request_format;
|
||||
}
|
||||
|
||||
foreach ($request_headers as $key => $value) {
|
||||
unset($request_headers[$key]);
|
||||
$key = strtoupper(str_replace('-', '_', $key));
|
||||
@@ -91,8 +86,13 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body);
|
||||
$route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format';
|
||||
$request = Request::create('/rest/test', $method, [], [], [], $request_headers, $request_body);
|
||||
// \Drupal\Core\StackMiddleware\NegotiationMiddleware normally takes care
|
||||
// of this so we'll hard code it here.
|
||||
if ($request_format) {
|
||||
$request->setRequestFormat($request_format);
|
||||
}
|
||||
$route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format';
|
||||
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $this->randomMachineName()], [$route_requirement_key_format => implode('|', $supported_formats)]));
|
||||
|
||||
$resource_response_subscriber = new ResourceResponseSubscriber(
|
||||
@@ -116,11 +116,6 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
|
||||
public function testOnResponseWithCacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) {
|
||||
$rest_config_name = $this->randomMachineName();
|
||||
|
||||
$parameters = [];
|
||||
if ($request_format !== FALSE) {
|
||||
$parameters['_format'] = $request_format;
|
||||
}
|
||||
|
||||
foreach ($request_headers as $key => $value) {
|
||||
unset($request_headers[$key]);
|
||||
$key = strtoupper(str_replace('-', '_', $key));
|
||||
@@ -128,8 +123,13 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body);
|
||||
$route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format';
|
||||
$request = Request::create('/rest/test', $method, [], [], [], $request_headers, $request_body);
|
||||
// \Drupal\Core\StackMiddleware\NegotiationMiddleware normally takes care
|
||||
// of this so we'll hard code it here.
|
||||
if ($request_format) {
|
||||
$request->setRequestFormat($request_format);
|
||||
}
|
||||
$route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format';
|
||||
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)]));
|
||||
|
||||
// The RequestHandler must return a ResourceResponseInterface object.
|
||||
@@ -166,11 +166,6 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
|
||||
public function testOnResponseWithUncacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) {
|
||||
$rest_config_name = $this->randomMachineName();
|
||||
|
||||
$parameters = [];
|
||||
if ($request_format !== FALSE) {
|
||||
$parameters['_format'] = $request_format;
|
||||
}
|
||||
|
||||
foreach ($request_headers as $key => $value) {
|
||||
unset($request_headers[$key]);
|
||||
$key = strtoupper(str_replace('-', '_', $key));
|
||||
@@ -178,8 +173,13 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body);
|
||||
$route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format';
|
||||
$request = Request::create('/rest/test', $method, [], [], [], $request_headers, $request_body);
|
||||
// \Drupal\Core\StackMiddleware\NegotiationMiddleware normally takes care
|
||||
// of this so we'll hard code it here.
|
||||
if ($request_format) {
|
||||
$request->setRequestFormat($request_format);
|
||||
}
|
||||
$route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format';
|
||||
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)]));
|
||||
|
||||
// The RequestHandler must return a ResourceResponseInterface object.
|
||||
|
||||
Reference in New Issue
Block a user