Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -4,6 +4,7 @@ rest.settings:
|
|||
label: 'REST settings'
|
||||
mapping:
|
||||
# @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
|
||||
# @see https://www.drupal.org/node/2830467
|
||||
link_domain:
|
||||
type: string
|
||||
label: 'Domain of the relation'
|
||||
|
|
|
@ -34,6 +34,8 @@ function hook_rest_resource_alter(&$definitions) {
|
|||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Use
|
||||
* hook_serialization_type_uri_alter() instead. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*
|
||||
* Modules may wish to alter the type URI generated for a resource based on the
|
||||
* context of the serializer/normalizer operation.
|
||||
*
|
||||
|
@ -54,13 +56,14 @@ function hook_rest_type_uri_alter(&$uri, $context = []) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Alter the REST relation URI.
|
||||
*
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Use
|
||||
* hook_serialization_relation_uri_alter() instead. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*
|
||||
* Modules may wish to alter the relation URI generated for a resource based on
|
||||
* the context of the serializer/normalizer operation.
|
||||
*
|
||||
|
|
|
@ -5,4 +5,4 @@ package: Web services
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- serialization
|
||||
- drupal:serialization
|
||||
|
|
|
@ -14,7 +14,7 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
|
|||
function rest_requirements($phase) {
|
||||
$requirements = [];
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.6.0', '>=') && version_compare(PHP_VERSION, '7', '<') && ini_get('always_populate_raw_post_data') != -1) {
|
||||
if ($phase == 'runtime' && PHP_SAPI !== 'cli' && version_compare(PHP_VERSION, '5.6.0', '>=') && version_compare(PHP_VERSION, '7', '<') && ini_get('always_populate_raw_post_data') != -1) {
|
||||
$requirements['always_populate_raw_post_data'] = [
|
||||
'title' => t('always_populate_raw_post_data PHP setting'),
|
||||
'value' => t('Not set to -1.'),
|
||||
|
@ -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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ function rest_post_update_resource_granularity() {
|
|||
$resource_config_entity->set('configuration', [
|
||||
'methods' => array_keys($configuration),
|
||||
'formats' => $configuration[$first_method]['supported_formats'],
|
||||
'authentication' => $configuration[$first_method]['supported_auth']
|
||||
'authentication' => $configuration[$first_method]['supported_auth'],
|
||||
]);
|
||||
$resource_config_entity->set('granularity', RestResourceConfigInterface::RESOURCE_GRANULARITY);
|
||||
$resource_config_entity->save();
|
||||
|
|
|
@ -31,3 +31,20 @@ 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 }
|
||||
rest.route_processor_get_bc:
|
||||
class: \Drupal\rest\RouteProcessor\RestResourceGetRouteProcessorBC
|
||||
arguments: ['%serializer.formats%', '@router.route_provider']
|
||||
tags:
|
||||
- { name: route_processor_outbound }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Drupal\rest\Annotation;
|
||||
|
||||
use \Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a REST resource annotation object.
|
||||
|
|
|
@ -13,6 +13,13 @@ use Drupal\rest\RestResourceConfigInterface;
|
|||
* @ConfigEntityType(
|
||||
* id = "rest_resource_config",
|
||||
* label = @Translation("REST resource configuration"),
|
||||
* label_collection = @Translation("REST resource configurations"),
|
||||
* label_singular = @Translation("REST resource configuration"),
|
||||
* label_plural = @Translation("REST resource configurations"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count REST resource configuration",
|
||||
* plural = "@count REST resource configurations",
|
||||
* ),
|
||||
* config_prefix = "resource",
|
||||
* admin_permission = "administer rest resources",
|
||||
* label_callback = "getLabelFromPlugin",
|
||||
|
@ -203,7 +210,7 @@ class RestResourceConfig extends ConfigEntityBase implements RestResourceConfigI
|
|||
*/
|
||||
public function getPluginCollections() {
|
||||
return [
|
||||
'resource' => new DefaultSingleLazyPluginCollection($this->getResourcePluginManager(), $this->plugin_id, [])
|
||||
'resource' => new DefaultSingleLazyPluginCollection($this->getResourcePluginManager(), $this->plugin_id, []),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
namespace Drupal\rest\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\CacheableResponse;
|
||||
use Drupal\Core\Cache\CacheableResponseInterface;
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\rest\ResourceResponseInterface;
|
||||
use Drupal\serialization\Normalizer\CacheableNormalizerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
|
@ -79,7 +81,7 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
|
|||
* Determines the format to respond in.
|
||||
*
|
||||
* Respects the requested format if one is specified. However, it is common to
|
||||
* forget to specify a request format in case of a POST or PATCH. Rather than
|
||||
* forget to specify a response format in case of a POST or PATCH. Rather than
|
||||
* simply throwing an error, we apply the robustness principle: when POSTing
|
||||
* or PATCHing using a certain format, you probably expect a response in that
|
||||
* same format.
|
||||
|
@ -94,43 +96,53 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
public function getResponseFormat(RouteMatchInterface $route_match, Request $request) {
|
||||
$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_response_formats = $route->hasRequirement('_format') ? explode('|', $route->getRequirement('_format')) : [];
|
||||
$acceptable_request_formats = $route->hasRequirement('_content_type_format') ? explode('|', $route->getRequirement('_content_type_format')) : [];
|
||||
$acceptable_formats = $request->isMethodCacheable() ? $acceptable_response_formats : $acceptable_request_formats;
|
||||
|
||||
$requested_format = $request->getRequestFormat();
|
||||
$content_type_format = $request->getContentType();
|
||||
|
||||
// If an acceptable format is requested, then use that. Otherwise, including
|
||||
// and particularly when the client forgot to specify a format, then use
|
||||
// heuristics to select the format that is most likely expected.
|
||||
if (in_array($requested_format, $acceptable_formats)) {
|
||||
// If an acceptable response format is requested, then use that. Otherwise,
|
||||
// including and particularly when the client forgot to specify a response
|
||||
// format, then use heuristics to select the format that is most likely
|
||||
// expected.
|
||||
if (in_array($requested_format, $acceptable_response_formats, TRUE)) {
|
||||
return $requested_format;
|
||||
}
|
||||
|
||||
// If a request body is present, then use the format corresponding to the
|
||||
// request body's Content-Type for the response, if it's an acceptable
|
||||
// format for the request.
|
||||
elseif (!empty($request->getContent()) && in_array($content_type_format, $acceptable_content_type_formats)) {
|
||||
if (!empty($request->getContent()) && in_array($content_type_format, $acceptable_request_formats, TRUE)) {
|
||||
return $content_type_format;
|
||||
}
|
||||
|
||||
// Otherwise, use the first acceptable format.
|
||||
elseif (!empty($acceptable_formats)) {
|
||||
if (!empty($acceptable_formats)) {
|
||||
return $acceptable_formats[0];
|
||||
}
|
||||
|
||||
// Sometimes, there are no acceptable formats, e.g. DELETE routes.
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a resource response body.
|
||||
*
|
||||
* Serialization can invoke rendering (e.g., generating URLs), but the
|
||||
* serialization API does not provide a mechanism to collect the
|
||||
* bubbleable metadata associated with that (e.g., language and other
|
||||
* contexts), so instead, allow those to "leak" and collect them here in
|
||||
* a render context.
|
||||
* During serialization, encoders and normalizers are able to explicitly
|
||||
* bubble cacheability metadata via the 'cacheability' key-value pair in the
|
||||
* received context. This bubbled cacheability metadata will be applied to the
|
||||
* the response.
|
||||
*
|
||||
* In versions of Drupal prior to 8.5, implicit bubbling of cacheability
|
||||
* metadata was allowed because there was no explicit cacheability metadata
|
||||
* bubbling API. To maintain backwards compatibility, we continue to support
|
||||
* this, but support for this will be dropped in Drupal 9.0.0. This is
|
||||
* especially useful when interacting with APIs that implicitly invoke
|
||||
* rendering (for example: generating URLs): this allows those to "leak", and
|
||||
* we collect their bubbled cacheability metadata automatically in a render
|
||||
* context.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
|
@ -150,14 +162,25 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
|
|||
|
||||
// If there is data to send, serialize and set it as the response body.
|
||||
if ($data !== NULL) {
|
||||
$serialization_context = [
|
||||
'request' => $request,
|
||||
CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY => new CacheableMetadata(),
|
||||
];
|
||||
|
||||
// @deprecated In Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
|
||||
// explicit cacheability metadata bubbling instead. (The wrapping call to
|
||||
// executeInRenderContext() will be removed before Drupal 9.0.0.)
|
||||
$context = new RenderContext();
|
||||
$output = $this->renderer
|
||||
->executeInRenderContext($context, function () use ($serializer, $data, $format) {
|
||||
return $serializer->serialize($data, $format);
|
||||
->executeInRenderContext($context, function () use ($serializer, $data, $format, $serialization_context) {
|
||||
return $serializer->serialize($data, $format, $serialization_context);
|
||||
});
|
||||
|
||||
if ($response instanceof CacheableResponseInterface && !$context->isEmpty()) {
|
||||
$response->addCacheableDependency($context->pop());
|
||||
if ($response instanceof CacheableResponseInterface) {
|
||||
if (!$context->isEmpty()) {
|
||||
@trigger_error('Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.5.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling. See https://www.drupal.org/node/2918937', E_USER_DEPRECATED);
|
||||
$response->addCacheableDependency($context->pop());
|
||||
}
|
||||
$response->addCacheableDependency($serialization_context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]);
|
||||
}
|
||||
|
||||
$response->setContent($output);
|
||||
|
@ -196,8 +219,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) {
|
||||
|
@ -37,6 +37,7 @@ class RestConfigSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
public function onSave(ConfigCrudEvent $event) {
|
||||
$saved_config = $event->getConfig();
|
||||
// @see \Drupal\rest\Plugin\rest\resource\EntityResource::permissions()
|
||||
if ($saved_config->getName() === 'rest.settings' && $event->isChanged('bc_entity_resource_permissions')) {
|
||||
$this->routerBuilder->setRebuildNeeded();
|
||||
}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\ConfigurableLinkManagerInterface as MovedConfigurable
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
interface ConfigurableLinkManagerInterface extends MovedConfigurableLinkManagerInterface {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\LinkManager as MovedLinkManager;
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
class LinkManager extends MovedLinkManager implements LinkManagerInterface {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\LinkManagerBase as MovedLinkManagerBase;
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
abstract class LinkManagerBase extends MovedLinkManagerBase {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\LinkManagerInterface as MovedLinkManagerInterface;
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
interface LinkManagerInterface extends MovedLinkManagerInterface {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\RelationLinkManager as MovedLinkRelationManager;
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
class RelationLinkManager extends MovedLinkRelationManager implements RelationLinkManagerInterface {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\RelationLinkManagerInterface as MovedRelationLinkMana
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
interface RelationLinkManagerInterface extends MovedRelationLinkManagerInterface {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\TypeLinkManager as MovedTypeLinkManager;
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
class TypeLinkManager extends MovedTypeLinkManager implements TypeLinkManagerInterface {}
|
||||
|
|
|
@ -7,5 +7,7 @@ use Drupal\hal\LinkManager\TypeLinkManagerInterface as MovedTypeLinkManagerInter
|
|||
/**
|
||||
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has
|
||||
* been moved to the hal module. This exists solely for BC.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2830467
|
||||
*/
|
||||
interface TypeLinkManagerInterface extends MovedTypeLinkManagerInterface {}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -65,6 +65,10 @@ class EntityDeriver implements ContainerDeriverInterface {
|
|||
if (!isset($this->derivatives)) {
|
||||
// Add in the default plugin configuration and the resource type.
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->isInternal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->derivatives[$entity_type_id] = [
|
||||
'id' => 'entity:' . $entity_type_id,
|
||||
'entity_type' => $entity_type_id,
|
||||
|
@ -74,7 +78,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) {
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\rest\Plugin;
|
|||
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Routing\BcRoute;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
@ -100,35 +101,38 @@ 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, ':', '.');
|
||||
|
||||
$methods = $this->availableMethods();
|
||||
foreach ($methods as $method) {
|
||||
$route = $this->getBaseRoute($canonical_path, $method);
|
||||
$path = $method === 'POST'
|
||||
? $create_path
|
||||
: $canonical_path;
|
||||
$route = $this->getBaseRoute($path, $method);
|
||||
|
||||
switch ($method) {
|
||||
case 'POST':
|
||||
$route->setPath($create_path);
|
||||
$collection->add("$route_name.$method", $route);
|
||||
break;
|
||||
// Note that '_format' and '_content_type_format' route requirements are
|
||||
// added in ResourceRoutes::getRoutesForResourceConfig().
|
||||
$collection->add("$route_name.$method", $route);
|
||||
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
// Restrict GET and HEAD requests to the media type specified in the
|
||||
// HTTP Accept headers.
|
||||
foreach ($this->serializerFormats as $format_name) {
|
||||
// Expose one route per available format.
|
||||
$format_route = clone $route;
|
||||
$format_route->addRequirements(['_format' => $format_name]);
|
||||
$collection->add("$route_name.$method.$format_name", $format_route);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$collection->add("$route_name.$method", $route);
|
||||
break;
|
||||
// BC: the REST module originally created per-format GET routes, instead
|
||||
// of a single route. To minimize the surface of this BC layer, this uses
|
||||
// route definitions that are as empty as possible, plus an outbound route
|
||||
// processor.
|
||||
// @see \Drupal\rest\RouteProcessor\RestResourceGetRouteProcessorBC
|
||||
if ($method === 'GET' || $method === 'HEAD') {
|
||||
foreach ($this->serializerFormats as $format_name) {
|
||||
$collection->add("$route_name.$method.$format_name", (new BcRoute())->setRequirement('_format', $format_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,10 @@ class ResourcePluginManager extends DefaultPluginManager {
|
|||
* @deprecated in Drupal 8.2.0.
|
||||
* Use Drupal\rest\Plugin\Type\ResourcePluginManager::createInstance()
|
||||
* instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2874934
|
||||
*/
|
||||
public function getInstance(array $options){
|
||||
public function getInstance(array $options) {
|
||||
if (isset($options['id'])) {
|
||||
return $this->createInstance($options['id']);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\rest\Plugin\rest\resource;
|
|||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Access\AccessResultReasonInterface;
|
||||
use Drupal\Core\Cache\CacheableResponseInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityType;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
|
@ -12,7 +13,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\TypedData\PrimitiveInterface;
|
||||
use Drupal\Core\Http\Exception\CacheableAccessDeniedHttpException;
|
||||
use Drupal\rest\Plugin\ResourceBase;
|
||||
use Drupal\rest\ResourceResponse;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -35,7 +36,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}"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
|
@ -122,7 +123,7 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
|||
public function get(EntityInterface $entity) {
|
||||
$entity_access = $entity->access('view', NULL, TRUE);
|
||||
if (!$entity_access->isAllowed()) {
|
||||
throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'view'));
|
||||
throw new CacheableAccessDeniedHttpException($entity_access, $entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'view'));
|
||||
}
|
||||
|
||||
$response = new ResourceResponse($entity, 200);
|
||||
|
@ -201,41 +202,6 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values from the field item list casted to the correct type.
|
||||
*
|
||||
* Values are casted to the correct type so we can determine whether or not
|
||||
* something has changed. REST formats such as JSON support typed data but
|
||||
* Drupal's database API will return values as strings. Currently, only
|
||||
* primitive data types know how to cast their values to the correct type.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldItemListInterface $field_item_list
|
||||
* The field item list to retrieve its data from.
|
||||
*
|
||||
* @return mixed[][]
|
||||
* The values from the field item list casted to the correct type. The array
|
||||
* of values returned is a multidimensional array keyed by delta and the
|
||||
* property name.
|
||||
*/
|
||||
protected function getCastedValueFromFieldItemList(FieldItemListInterface $field_item_list) {
|
||||
$value = $field_item_list->getValue();
|
||||
|
||||
foreach ($value as $delta => $field_item_value) {
|
||||
/** @var \Drupal\Core\Field\FieldItemInterface $field_item */
|
||||
$field_item = $field_item_list->get($delta);
|
||||
$properties = $field_item->getProperties(TRUE);
|
||||
// Foreach field value we check whether we know the underlying property.
|
||||
// If we exists we try to cast the value.
|
||||
foreach ($field_item_value as $property_name => $property_value) {
|
||||
if (isset($properties[$property_name]) && ($property = $field_item->get($property_name)) && $property instanceof PrimitiveInterface) {
|
||||
$value[$delta][$property_name] = $property->getCastedValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to entity PATCH requests.
|
||||
*
|
||||
|
@ -262,42 +228,30 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
|||
throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'update'));
|
||||
}
|
||||
|
||||
// Overwrite the received properties.
|
||||
$entity_keys = $entity->getEntityType()->getKeys();
|
||||
// Overwrite the received fields.
|
||||
// @todo Remove $changed_fields in https://www.drupal.org/project/drupal/issues/2862574.
|
||||
$changed_fields = [];
|
||||
foreach ($entity->_restSubmittedFields as $field_name) {
|
||||
$field = $entity->get($field_name);
|
||||
|
||||
// Entity key fields need special treatment: together they uniquely
|
||||
// identify the entity. Therefore it does not make sense to modify any of
|
||||
// them. However, rather than throwing an error, we just ignore them as
|
||||
// long as their specified values match their current values.
|
||||
if (in_array($field_name, $entity_keys, TRUE)) {
|
||||
// @todo Work around the wrong assumption that entity keys need special
|
||||
// treatment, when only read-only fields need it.
|
||||
// This will be fixed in https://www.drupal.org/node/2824851.
|
||||
if ($entity->getEntityTypeId() == 'comment' && $field_name == 'status' && !$original_entity->get($field_name)->access('edit')) {
|
||||
throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
|
||||
}
|
||||
|
||||
// Unchanged values for entity keys don't need access checking.
|
||||
if ($this->getCastedValueFromFieldItemList($original_entity->get($field_name)) === $this->getCastedValueFromFieldItemList($entity->get($field_name))) {
|
||||
continue;
|
||||
}
|
||||
// It is not possible to set the language to NULL as it is automatically
|
||||
// re-initialized. As it must not be empty, skip it if it is.
|
||||
elseif (isset($entity_keys['langcode']) && $field_name === $entity_keys['langcode'] && $field->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// It is not possible to set the language to NULL as it is automatically
|
||||
// re-initialized. As it must not be empty, skip it if it is.
|
||||
// @todo Remove in https://www.drupal.org/project/drupal/issues/2933408.
|
||||
if ($entity->getEntityType()->hasKey('langcode') && $field_name === $entity->getEntityType()->getKey('langcode') && $field->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$original_entity->get($field_name)->access('edit')) {
|
||||
throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
|
||||
if ($this->checkPatchFieldAccess($original_entity->get($field_name), $field)) {
|
||||
$changed_fields[] = $field_name;
|
||||
$original_entity->set($field_name, $field->getValue());
|
||||
}
|
||||
$original_entity->set($field_name, $field->getValue());
|
||||
}
|
||||
|
||||
// If no fields are changed, we can send a response immediately!
|
||||
if (empty($changed_fields)) {
|
||||
return new ModifiedResourceResponse($original_entity, 200);
|
||||
}
|
||||
|
||||
// Validate the received data before saving.
|
||||
$this->validate($original_entity);
|
||||
$this->validate($original_entity, $changed_fields);
|
||||
try {
|
||||
$original_entity->save();
|
||||
$this->logger->notice('Updated entity %type with ID %id.', ['%type' => $original_entity->getEntityTypeId(), '%id' => $original_entity->id()]);
|
||||
|
@ -310,6 +264,57 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given field should be PATCHed.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldItemListInterface $original_field
|
||||
* The original (stored) value for the field.
|
||||
* @param \Drupal\Core\Field\FieldItemListInterface $received_field
|
||||
* The received value for the field.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the field should be PATCHed or not.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Thrown when the user sending the request is not allowed to update the
|
||||
* field. Only thrown when the user could not abuse this information to
|
||||
* determine the stored value.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function checkPatchFieldAccess(FieldItemListInterface $original_field, FieldItemListInterface $received_field) {
|
||||
// The user might not have access to edit the field, but still needs to
|
||||
// submit the current field value as part of the PATCH request. For
|
||||
// example, the entity keys required by denormalizers. Therefore, if the
|
||||
// received value equals the stored value, return FALSE without throwing an
|
||||
// exception. But only for fields that the user has access to view, because
|
||||
// the user has no legitimate way of knowing the current value of fields
|
||||
// that they are not allowed to view, and we must not make the presence or
|
||||
// absence of a 403 response a way to find that out.
|
||||
if ($original_field->access('view') && $original_field->equals($received_field)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If the user is allowed to edit the field, it is always safe to set the
|
||||
// received value. We may be setting an unchanged value, but that is ok.
|
||||
$field_edit_access = $original_field->access('edit', NULL, TRUE);
|
||||
if ($field_edit_access->isAllowed()) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// It's helpful and safe to let the user know when they are not allowed to
|
||||
// update a field.
|
||||
$field_name = $received_field->getName();
|
||||
$error_message = "Access denied on updating field '$field_name'.";
|
||||
if ($field_edit_access instanceof AccessResultReasonInterface) {
|
||||
$reason = $field_edit_access->getReason();
|
||||
if ($reason) {
|
||||
$error_message .= ' ' . $reason;
|
||||
}
|
||||
}
|
||||
throw new AccessDeniedHttpException($error_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to entity DELETE requests.
|
||||
*
|
||||
|
@ -431,8 +436,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 +448,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);
|
||||
|
|
|
@ -14,16 +14,23 @@ use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
|||
trait EntityResourceValidationTrait {
|
||||
|
||||
/**
|
||||
* Verifies that the whole entity does not violate any validation constraints.
|
||||
* Verifies that an entity does not violate any validation constraints.
|
||||
*
|
||||
* The validation errors will be filtered to not include fields to which the
|
||||
* current user does not have access and if $fields_to_validate is provided
|
||||
* will only include fields in that array.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to validate.
|
||||
* @param string[] $fields_to_validate
|
||||
* (optional) An array of field names. If specified, filters the violations
|
||||
* list to include only this set of fields.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
|
||||
* If validation errors are found.
|
||||
*/
|
||||
protected function validate(EntityInterface $entity) {
|
||||
// @todo Remove when https://www.drupal.org/node/2164373 is committed.
|
||||
protected function validate(EntityInterface $entity, array $fields_to_validate = []) {
|
||||
// @todo Update this check in https://www.drupal.org/node/2300677.
|
||||
if (!$entity instanceof FieldableEntityInterface) {
|
||||
return;
|
||||
}
|
||||
|
@ -33,6 +40,11 @@ trait EntityResourceValidationTrait {
|
|||
// changes.
|
||||
$violations->filterByFieldAccess();
|
||||
|
||||
if ($fields_to_validate) {
|
||||
// Filter violations by explicitly provided array of field names.
|
||||
$violations->filterByFields(array_diff(array_keys($entity->getFieldDefinitions()), $fields_to_validate));
|
||||
}
|
||||
|
||||
if ($violations->count() > 0) {
|
||||
$message = "Unprocessable Entity: validation failed.\n";
|
||||
foreach ($violations as $violation) {
|
||||
|
|
|
@ -70,7 +70,7 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mimeType;
|
||||
protected $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
|
@ -87,12 +87,35 @@ 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[]
|
||||
*/
|
||||
protected $authenticationProviders;
|
||||
|
||||
/**
|
||||
* The serialization format providers, keyed by format.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $formatProviders;
|
||||
|
||||
/**
|
||||
* Constructs a RestExport object.
|
||||
*
|
||||
|
@ -110,12 +133,22 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
|||
* The renderer.
|
||||
* @param string[] $authentication_providers
|
||||
* The authentication providers, keyed by ID.
|
||||
* @param string[] $serializer_format_providers
|
||||
* The serialization format providers, keyed by format.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, RendererInterface $renderer, array $authentication_providers) {
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, RendererInterface $renderer, array $authentication_providers, array $serializer_format_providers) {
|
||||
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;
|
||||
$this->formatProviders = $serializer_format_providers;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,31 +162,33 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
|||
$container->get('router.route_provider'),
|
||||
$container->get('state'),
|
||||
$container->get('renderer'),
|
||||
$container->getParameter('authentication_providers')
|
||||
|
||||
$container->getParameter('authentication_providers'),
|
||||
$container->getParameter('serializer.format_providers')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) {
|
||||
parent::initDisplay($view, $display, $options);
|
||||
|
||||
$request_content_type = $this->view->getRequest()->getRequestFormat();
|
||||
// Only use the requested content type if it's not 'html'. If it is then
|
||||
// default to 'json' to aid debugging.
|
||||
// @todo Remove the need for this when we have better content negotiation.
|
||||
if ($request_content_type != 'html') {
|
||||
$this->setContentType($request_content_type);
|
||||
}
|
||||
// If the requested content type is 'html' and the default 'json' is not
|
||||
// selected as a format option in the view display, fallback to the first
|
||||
// format in the array.
|
||||
elseif (!empty($options['style']['options']['formats']) && !isset($options['style']['options']['formats'][$this->getContentType()])) {
|
||||
$this->setContentType(reset($options['style']['options']['formats']));
|
||||
// If the default 'json' format is not selected as a format option in the
|
||||
// view display, fallback to the first format available for the default.
|
||||
if (!empty($options['style']['options']['formats']) && !isset($options['style']['options']['formats'][$this->getContentType()])) {
|
||||
$default_format = reset($options['style']['options']['formats']);
|
||||
$this->setContentType($default_format);
|
||||
}
|
||||
|
||||
$this->setMimeType($this->view->getRequest()->getMimeType($this->contentType));
|
||||
// Only use the requested content type if it's not 'html'. This allows
|
||||
// still falling back to the default for things like views preview.
|
||||
$request_content_type = $this->view->getRequest()->getRequestFormat();
|
||||
|
||||
if ($request_content_type !== 'html') {
|
||||
$this->setContentType($request_content_type);
|
||||
}
|
||||
|
||||
$this->setMimeType($this->view->getRequest()->getMimeType($this->getContentType()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,7 +262,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -327,17 +362,21 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
|||
|
||||
if ($route = $collection->get("view.$view_id.$display_id")) {
|
||||
$style_plugin = $this->getPlugin('style');
|
||||
// REST exports should only respond to get methods.
|
||||
|
||||
// REST exports should only respond to GET methods.
|
||||
$route->setMethods(['GET']);
|
||||
|
||||
// Format as a string using pipes as a delimiter.
|
||||
if ($formats = $style_plugin->getFormats()) {
|
||||
// Allow a REST Export View to be returned with an HTML-only accept
|
||||
// format. That allows browsers or other non-compliant systems to access
|
||||
// the view, as it is unlikely to have a conflicting HTML representation
|
||||
// anyway.
|
||||
$route->setRequirement('_format', implode('|', $formats + ['html']));
|
||||
$formats = $style_plugin->getFormats();
|
||||
|
||||
// If there are no configured formats, add all formats that serialization
|
||||
// is known to support.
|
||||
if (!$formats) {
|
||||
$formats = $this->getFormatOptions();
|
||||
}
|
||||
|
||||
// Format as a string using pipes as a delimiter.
|
||||
$route->setRequirement('_format', implode('|', $formats));
|
||||
|
||||
// Add authentication to the route if it was set. If no authentication was
|
||||
// set, the default authentication will be used, which is cookie based by
|
||||
// default.
|
||||
|
@ -407,7 +446,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();
|
||||
});
|
||||
|
||||
|
@ -421,21 +460,21 @@ class RestExport extends PathPluginBase implements ResponseDisplayPluginInterfac
|
|||
$build['#suffix'] = '</pre>';
|
||||
unset($build['#markup']);
|
||||
}
|
||||
elseif ($this->view->getRequest()->getFormat($this->view->element['#content_type']) !== 'html') {
|
||||
// This display plugin is primarily for returning non-HTML formats.
|
||||
// However, we still invoke the renderer to collect cacheability metadata.
|
||||
// Because the renderer is designed for HTML rendering, it filters
|
||||
// #markup for XSS unless it is already known to be safe, but that filter
|
||||
// only works for HTML. Therefore, we mark the contents as safe to bypass
|
||||
// the filter. So long as we are returning this in a non-HTML response
|
||||
// (checked above), this is safe, because an XSS attack only works when
|
||||
// executed by an HTML agent.
|
||||
else {
|
||||
// This display plugin is for returning non-HTML formats. However, we
|
||||
// still invoke the renderer to collect cacheability metadata. Because the
|
||||
// renderer is designed for HTML rendering, it filters #markup for XSS
|
||||
// unless it is already known to be safe, but that filter only works for
|
||||
// HTML. Therefore, we mark the contents as safe to bypass the filter. So
|
||||
// long as we are returning this in a non-HTML response,
|
||||
// this is safe, because an XSS attack only works when executed by an HTML
|
||||
// agent.
|
||||
// @todo Decide how to support non-HTML in the render API in
|
||||
// https://www.drupal.org/node/2501313.
|
||||
$build['#markup'] = ViewsRenderPipelineMarkup::create($build['#markup']);
|
||||
}
|
||||
|
||||
parent::applyDisplayCachablityMetadata($build);
|
||||
parent::applyDisplayCacheabilityMetadata($build);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
@ -457,12 +496,27 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of format options.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of format options. Both key and value are the same.
|
||||
*/
|
||||
protected function getFormatOptions() {
|
||||
$formats = array_keys($this->formatProviders);
|
||||
return array_combine($formats, $formats);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2,110 +2,330 @@
|
|||
|
||||
namespace Drupal\rest;
|
||||
|
||||
use Drupal\Component\Utility\ArgumentsResolver;
|
||||
use Drupal\Core\Cache\CacheableResponseInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Drupal\rest\Plugin\ResourceInterface;
|
||||
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;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* Acts as intermediate request forwarder for resource plugins.
|
||||
*
|
||||
* @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
|
||||
*/
|
||||
class RequestHandler implements ContainerAwareInterface, ContainerInjectionInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
class RequestHandler implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The resource configuration storage.
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $resourceStorage;
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The serializer.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\SerializerInterface|\Symfony\Component\Serializer\Encoder\DecoderInterface
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* Creates a new RequestHandler instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
|
||||
* The resource configuration storage.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Symfony\Component\Serializer\SerializerInterface|\Symfony\Component\Serializer\Encoder\DecoderInterface $serializer
|
||||
* The serializer.
|
||||
*/
|
||||
public function __construct(EntityStorageInterface $entity_storage) {
|
||||
$this->resourceStorage = $entity_storage;
|
||||
public function __construct(ConfigFactoryInterface $config_factory, SerializerInterface $serializer) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity_type.manager')->getStorage('rest_resource_config'));
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('serializer')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a web API request.
|
||||
* Handles a REST API request.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HTTP request object.
|
||||
* @param \Drupal\rest\RestResourceConfigInterface $_rest_resource_config
|
||||
* The REST resource config entity.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* The response object.
|
||||
* @return \Drupal\rest\ResourceResponseInterface|\Symfony\Component\HttpFoundation\Response
|
||||
* The REST resource response.
|
||||
*/
|
||||
public function handle(RouteMatchInterface $route_match, Request $request) {
|
||||
$method = strtolower($request->getMethod());
|
||||
public function handle(RouteMatchInterface $route_match, Request $request, RestResourceConfigInterface $_rest_resource_config) {
|
||||
$resource = $_rest_resource_config->getResourcePlugin();
|
||||
$unserialized = $this->deserialize($route_match, $request, $resource);
|
||||
$response = $this->delegateToRestResourcePlugin($route_match, $request, $unserialized, $resource);
|
||||
return $this->prepareResponse($response, $_rest_resource_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a REST API request without deserializing the request body.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HTTP request object.
|
||||
* @param \Drupal\rest\RestResourceConfigInterface $_rest_resource_config
|
||||
* The REST resource config entity.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response|\Drupal\rest\ResourceResponseInterface
|
||||
* The REST resource response.
|
||||
*/
|
||||
public function handleRaw(RouteMatchInterface $route_match, Request $request, RestResourceConfigInterface $_rest_resource_config) {
|
||||
$resource = $_rest_resource_config->getResourcePlugin();
|
||||
$response = $this->delegateToRestResourcePlugin($route_match, $request, NULL, $resource);
|
||||
return $this->prepareResponse($response, $_rest_resource_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the REST resource response.
|
||||
*
|
||||
* @param \Drupal\rest\ResourceResponseInterface $response
|
||||
* The REST resource response.
|
||||
* @param \Drupal\rest\RestResourceConfigInterface $resource_config
|
||||
* The REST resource config entity.
|
||||
*
|
||||
* @return \Drupal\rest\ResourceResponseInterface
|
||||
* The prepared REST resource response.
|
||||
*/
|
||||
protected function prepareResponse($response, RestResourceConfigInterface $resource_config) {
|
||||
if ($response instanceof CacheableResponseInterface) {
|
||||
$response->addCacheableDependency($resource_config);
|
||||
// Add global rest settings config's cache tag, for BC flags.
|
||||
// @see \Drupal\rest\Plugin\rest\resource\EntityResource::permissions()
|
||||
// @see \Drupal\rest\EventSubscriber\RestConfigSubscriber
|
||||
// @todo Remove in https://www.drupal.org/node/2893804
|
||||
$response->addCacheableDependency($this->configFactory->get('rest.settings'));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the normalized HTTP request method of the matched route.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
*
|
||||
* @return string
|
||||
* The normalized HTTP request method.
|
||||
*/
|
||||
protected static function getNormalizedRequestMethod(RouteMatchInterface $route_match) {
|
||||
// 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';
|
||||
}
|
||||
|
||||
$resource_config_id = $route_match->getRouteObject()->getDefault('_rest_resource_config');
|
||||
/** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
|
||||
$resource_config = $this->resourceStorage->load($resource_config_id);
|
||||
$resource = $resource_config->getResourcePlugin();
|
||||
$method = strtolower($route_match->getRouteObject()->getMethods()[0]);
|
||||
assert(count($route_match->getRouteObject()->getMethods()) === 1);
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes request body, if any.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HTTP request object.
|
||||
* @param \Drupal\rest\Plugin\ResourceInterface $resource
|
||||
* The REST resource plugin.
|
||||
*
|
||||
* @return array|null
|
||||
* An object normalization, ikf there is a valid request body. NULL if there
|
||||
* is no request body.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
|
||||
* Thrown if the request body cannot be decoded.
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
|
||||
* Thrown if the request body cannot be denormalized.
|
||||
*/
|
||||
protected function deserialize(RouteMatchInterface $route_match, Request $request, ResourceInterface $resource) {
|
||||
// Deserialize incoming data if available.
|
||||
/** @var \Symfony\Component\Serializer\SerializerInterface $serializer */
|
||||
$serializer = $this->container->get('serializer');
|
||||
$received = $request->getContent();
|
||||
$unserialized = NULL;
|
||||
if (!empty($received)) {
|
||||
$method = static::getNormalizedRequestMethod($route_match);
|
||||
$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 = $this->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 = $this->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $unserialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates an incoming request to the appropriate REST resource plugin.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HTTP request object.
|
||||
* @param mixed|null $unserialized
|
||||
* The unserialized request body, if any.
|
||||
* @param \Drupal\rest\Plugin\ResourceInterface $resource
|
||||
* The REST resource plugin.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response|\Drupal\rest\ResourceResponseInterface
|
||||
* The REST resource response.
|
||||
*/
|
||||
protected function delegateToRestResourcePlugin(RouteMatchInterface $route_match, Request $request, $unserialized, ResourceInterface $resource) {
|
||||
$method = static::getNormalizedRequestMethod($route_match);
|
||||
|
||||
// Determine the request parameters that should be passed to the resource
|
||||
// plugin.
|
||||
$argument_resolver = $this->createArgumentResolver($route_match, $unserialized, $request);
|
||||
try {
|
||||
$arguments = $argument_resolver->getArguments([$resource, $method]);
|
||||
}
|
||||
catch (\RuntimeException $exception) {
|
||||
@trigger_error('Passing in arguments the legacy way is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Provide the right parameter names in the method, similar to controllers. See https://www.drupal.org/node/2894819', E_USER_DEPRECATED);
|
||||
$arguments = $this->getLegacyParameters($route_match, $unserialized, $request);
|
||||
}
|
||||
|
||||
// Invoke the operation on the resource plugin.
|
||||
return call_user_func_array([$resource, $method], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an argument resolver, containing all REST parameters.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param mixed $unserialized
|
||||
* The unserialized data.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request.
|
||||
*
|
||||
* @return \Drupal\Component\Utility\ArgumentsResolver
|
||||
* An instance of the argument resolver containing information like the
|
||||
* 'entity' we process and the 'unserialized' content from the request body.
|
||||
*/
|
||||
protected function createArgumentResolver(RouteMatchInterface $route_match, $unserialized, Request $request) {
|
||||
$route = $route_match->getRouteObject();
|
||||
|
||||
// Defaults for the parameters defined on the route object need to be added
|
||||
// to the raw arguments.
|
||||
$raw_route_arguments = $route_match->getRawParameters()->all() + $route->getDefaults();
|
||||
|
||||
$route_arguments = $route_match->getParameters()->all();
|
||||
$upcasted_route_arguments = $route_arguments;
|
||||
|
||||
// For request methods that have request bodies, ResourceInterface plugin
|
||||
// methods historically receive the unserialized request body as the N+1th
|
||||
// method argument, where N is the number of route parameters specified on
|
||||
// the accompanying route. To be able to use the argument resolver, which is
|
||||
// not based on position but on name and typehint, specify commonly used
|
||||
// names here. Similarly, those methods receive the original stored object
|
||||
// as the first method argument.
|
||||
|
||||
$route_arguments_entity = NULL;
|
||||
// Try to find a parameter which is an entity.
|
||||
foreach ($route_arguments as $value) {
|
||||
if ($value instanceof EntityInterface) {
|
||||
$route_arguments_entity = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($request->getMethod(), ['PATCH', 'POST'], TRUE)) {
|
||||
$upcasted_route_arguments['entity'] = $unserialized;
|
||||
$upcasted_route_arguments['data'] = $unserialized;
|
||||
$upcasted_route_arguments['unserialized'] = $unserialized;
|
||||
$upcasted_route_arguments['original_entity'] = $route_arguments_entity;
|
||||
}
|
||||
else {
|
||||
$upcasted_route_arguments['entity'] = $route_arguments_entity;
|
||||
}
|
||||
|
||||
// Parameters which are not defined on the route object, but still are
|
||||
// essential for access checking are passed as wildcards to the argument
|
||||
// resolver.
|
||||
$wildcard_arguments = [$route, $route_match];
|
||||
$wildcard_arguments[] = $request;
|
||||
if (isset($unserialized)) {
|
||||
$wildcard_arguments[] = $unserialized;
|
||||
}
|
||||
|
||||
return new ArgumentsResolver($raw_route_arguments, $upcasted_route_arguments, $wildcard_arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the parameter usable without an argument resolver.
|
||||
*
|
||||
* This creates an list of parameters in a statically defined order.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match
|
||||
* @param mixed $unserialized
|
||||
* The unserialized data.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request.
|
||||
*
|
||||
* @deprecated in Drupal 8.4.0, will be removed before Drupal 9.0.0. Use the
|
||||
* argument resolver method instead, see ::createArgumentResolver().
|
||||
*
|
||||
* @see https://www.drupal.org/node/2894819
|
||||
*
|
||||
* @return array
|
||||
* An array of parameters.
|
||||
*/
|
||||
protected function getLegacyParameters(RouteMatchInterface $route_match, $unserialized, Request $request) {
|
||||
$route_parameters = $route_match->getParameters();
|
||||
$parameters = [];
|
||||
// Filter out all internal parameters starting with "_".
|
||||
|
@ -115,15 +335,7 @@ class RequestHandler implements ContainerAwareInterface, ContainerInjectionInter
|
|||
}
|
||||
}
|
||||
|
||||
// Invoke the operation on the resource plugin.
|
||||
$response = call_user_func_array([$resource, $method], array_merge($parameters, [$unserialized, $request]));
|
||||
|
||||
if ($response instanceof CacheableResponseInterface) {
|
||||
// Add rest config's cache tags.
|
||||
$response->addCacheableDependency($resource_config);
|
||||
}
|
||||
|
||||
return $response;
|
||||
return array_merge($parameters, [$unserialized, $request]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\rest;
|
||||
|
||||
|
||||
trait ResourceResponseTrait {
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@ use Drupal\Core\DependencyInjection\ServiceProviderInterface;
|
|||
use Drupal\rest\LinkManager\LinkManager;
|
||||
use Drupal\rest\LinkManager\RelationLinkManager;
|
||||
use Drupal\rest\LinkManager\TypeLinkManager;
|
||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
|
@ -27,19 +27,22 @@ class RestServiceProvider implements ServiceProviderInterface {
|
|||
if (isset($modules['hal'])) {
|
||||
// @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
|
||||
// Use hal.link_manager instead.
|
||||
$service_definition = new DefinitionDecorator(new Reference('hal.link_manager'));
|
||||
// @see https://www.drupal.org/node/2830467
|
||||
$service_definition = new ChildDefinition(new Reference('hal.link_manager'));
|
||||
$service_definition->setClass(LinkManager::class);
|
||||
$container->setDefinition('rest.link_manager', $service_definition);
|
||||
|
||||
// @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
|
||||
// Use hal.link_manager.type instead.
|
||||
$service_definition = new DefinitionDecorator(new Reference('hal.link_manager.type'));
|
||||
// @see https://www.drupal.org/node/2830467
|
||||
$service_definition = new ChildDefinition(new Reference('hal.link_manager.type'));
|
||||
$service_definition->setClass(TypeLinkManager::class);
|
||||
$container->setDefinition('rest.link_manager.type', $service_definition);
|
||||
|
||||
// @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
|
||||
// Use hal.link_manager.relation instead.
|
||||
$service_definition = new DefinitionDecorator(new Reference('hal.link_manager.relation'));
|
||||
// @see https://www.drupal.org/node/2830467
|
||||
$service_definition = new ChildDefinition(new Reference('hal.link_manager.relation'));
|
||||
$service_definition->setClass(RelationLinkManager::class);
|
||||
$container->setDefinition('rest.link_manager.relation', $service_definition);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest\RouteProcessor;
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Processes the BC REST routes, to ensure old route names continue to work.
|
||||
*/
|
||||
class RestResourceGetRouteProcessorBC implements OutboundRouteProcessorInterface {
|
||||
|
||||
/**
|
||||
* The available serialization formats.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $serializerFormats = [];
|
||||
|
||||
/**
|
||||
* The route provider.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* Constructs a RestResourceGetRouteProcessorBC object.
|
||||
*
|
||||
* @param string[] $serializer_formats
|
||||
* The available serialization formats.
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider.
|
||||
*/
|
||||
public function __construct(array $serializer_formats, RouteProviderInterface $route_provider) {
|
||||
$this->serializerFormats = $serializer_formats;
|
||||
$this->routeProvider = $route_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
$route_name_parts = explode('.', $route_name);
|
||||
// BC: the REST module originally created per-format GET routes, instead
|
||||
// of a single route. To minimize the surface of this BC layer, this uses
|
||||
// route definitions that are as empty as possible, plus an outbound route
|
||||
// processor.
|
||||
// @see \Drupal\rest\Plugin\ResourceBase::routes()
|
||||
if ($route_name_parts[0] === 'rest' && $route_name_parts[count($route_name_parts) - 2] === 'GET' && in_array($route_name_parts[count($route_name_parts) - 1], $this->serializerFormats, TRUE)) {
|
||||
array_pop($route_name_parts);
|
||||
$redirected_route_name = implode('.', $route_name_parts);
|
||||
@trigger_error(sprintf("The '%s' route is deprecated since version 8.5.x and will be removed in 9.0.0. Use the '%s' route instead.", $route_name, $redirected_route_name), E_USER_DEPRECATED);
|
||||
static::overwriteRoute($route, $this->routeProvider->getRouteByName($redirected_route_name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites one route's metadata with the other's.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $target_route
|
||||
* The route whose metadata to overwrite.
|
||||
* @param \Symfony\Component\Routing\Route $source_route
|
||||
* The route whose metadata to read from.
|
||||
*
|
||||
* @see \Symfony\Component\Routing\Route
|
||||
*/
|
||||
protected static function overwriteRoute(Route $target_route, Route $source_route) {
|
||||
$target_route->setPath($source_route->getPath());
|
||||
$target_route->setDefaults($source_route->getDefaults());
|
||||
$target_route->setRequirements($source_route->getRequirements());
|
||||
$target_route->setOptions($source_route->getOptions());
|
||||
$target_route->setHost($source_route->getHost());
|
||||
$target_route->setSchemes($source_route->getSchemes());
|
||||
$target_route->setMethods($source_route->getMethods());
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +92,11 @@ class ResourceRoutes extends RouteSubscriberBase {
|
|||
/** @var \Symfony\Component\Routing\Route $route */
|
||||
// @todo: Are multiple methods possible here?
|
||||
$methods = $route->getMethods();
|
||||
// Only expose routes where the method is enabled in the configuration.
|
||||
if ($methods && ($method = $methods[0]) && $supported_formats = $rest_resource_config->getFormats($method)) {
|
||||
// Only expose routes
|
||||
// - that have an explicit method and allow >=1 format for that method
|
||||
// - that exist for BC
|
||||
// @see \Drupal\rest\RouteProcessor\RestResourceGetRouteProcessorBC
|
||||
if (($methods && ($method = $methods[0]) && $supported_formats = $rest_resource_config->getFormats($method)) || $route->hasOption('bc_route')) {
|
||||
$route->setRequirement('_csrf_request_header_token', 'TRUE');
|
||||
|
||||
// Check that authentication providers are defined.
|
||||
|
@ -106,24 +111,34 @@ class ResourceRoutes extends RouteSubscriberBase {
|
|||
continue;
|
||||
}
|
||||
|
||||
// If the route has a format requirement, then verify that the
|
||||
// resource has it.
|
||||
$format_requirement = $route->getRequirement('_format');
|
||||
if ($format_requirement && !in_array($format_requirement, $rest_resource_config->getFormats($method))) {
|
||||
continue;
|
||||
// Remove BC routes for unsupported formats.
|
||||
if ($route->getOption('bc_route') === TRUE) {
|
||||
$format_requirement = $route->getRequirement('_format');
|
||||
if ($format_requirement && !in_array($format_requirement, $rest_resource_config->getFormats($method))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// The configuration has been validated, so we update the route to:
|
||||
// - set the allowed response body content types/formats for methods
|
||||
// that may send response bodies (unless hardcoded by the plugin)
|
||||
// - set the allowed request body content types/formats for methods that
|
||||
// allow request bodies to be sent
|
||||
// allow request bodies to be sent (unless hardcoded by the plugin)
|
||||
// - set the allowed authentication providers
|
||||
if (in_array($method, ['POST', 'PATCH', 'PUT'], TRUE)) {
|
||||
// Restrict the incoming HTTP Content-type header to the allowed
|
||||
// formats.
|
||||
if (in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'PATCH'], TRUE) && !$route->hasRequirement('_format')) {
|
||||
$route->addRequirements(['_format' => implode('|', $rest_resource_config->getFormats($method))]);
|
||||
}
|
||||
if (in_array($method, ['POST', 'PATCH', 'PUT'], TRUE) && !$route->hasRequirement('_content_type_format')) {
|
||||
$route->addRequirements(['_content_type_format' => implode('|', $rest_resource_config->getFormats($method))]);
|
||||
}
|
||||
$route->setOption('_auth', $rest_resource_config->getAuthenticationProviders($method));
|
||||
$route->setDefault('_rest_resource_config', $rest_resource_config->id());
|
||||
$parameters = $route->getOption('parameters') ?: [];
|
||||
$route->setOption('parameters', $parameters + [
|
||||
'_rest_resource_config' => [
|
||||
'type' => 'entity:' . $rest_resource_config->getEntityTypeId(),
|
||||
],
|
||||
]);
|
||||
$collection->add("rest.$name", $route);
|
||||
}
|
||||
|
||||
|
@ -131,4 +146,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 [
|
||||
|
@ -381,7 +383,7 @@ abstract class RESTTestBase extends WebTestBase {
|
|||
$resource_config = $this->resourceConfigStorage->create([
|
||||
'id' => $resource_config_id,
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => []
|
||||
'configuration' => [],
|
||||
]);
|
||||
}
|
||||
$configuration = $resource_config->get('configuration');
|
||||
|
@ -560,7 +562,7 @@ abstract class RESTTestBase extends WebTestBase {
|
|||
* The first value to check.
|
||||
* @param $message
|
||||
* (optional) A message to display with the assertion. Do not translate
|
||||
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
|
||||
* messages: use \Drupal\Component\Render\FormattableMarkup to embed
|
||||
* variables in the message text, not t(). If left blank, a default message
|
||||
* will be displayed.
|
||||
* @param $group
|
||||
|
|
|
@ -54,6 +54,6 @@ $data = $connection->insert('config')
|
|||
->fields([
|
||||
'name' => 'rest.settings',
|
||||
'data' => serialize($config),
|
||||
'collection' => ''
|
||||
'collection' => '',
|
||||
])
|
||||
->execute();
|
||||
|
|
|
@ -53,6 +53,6 @@ $data = $connection->insert('config')
|
|||
->fields([
|
||||
'name' => 'rest.settings',
|
||||
'data' => serialize($config),
|
||||
'collection' => ''
|
||||
'collection' => '',
|
||||
])
|
||||
->execute();
|
||||
|
|
63
web/core/modules/rest/tests/fixtures/update/rest-export-with-authentication-correction.php
vendored
Normal file
63
web/core/modules/rest/tests/fixtures/update/rest-export-with-authentication-correction.php
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture for \Drupal\Tests\rest\Functional\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();
|
|
@ -4,4 +4,4 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_test
|
||||
- drupal:config_test
|
||||
|
|
|
@ -4,5 +4,3 @@ description: 'Provides test hooks and resources for REST module.'
|
|||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- rest
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Contains hook implementations for testing REST module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
@ -14,19 +16,56 @@ use Drupal\Core\Access\AccessResult;
|
|||
* Implements hook_entity_field_access().
|
||||
*
|
||||
* @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::setUp()
|
||||
* @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPost()
|
||||
*/
|
||||
function rest_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
|
||||
// @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPost()
|
||||
// @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPatch()
|
||||
if ($field_definition->getName() === 'field_rest_test') {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
// Never ever allow this field to be viewed: this lets EntityResourceTestBase::testGet() test in a "vanilla" way.
|
||||
// Never ever allow this field to be viewed: this lets
|
||||
// EntityResourceTestBase::testGet() test in a "vanilla" way.
|
||||
return AccessResult::forbidden();
|
||||
case 'edit':
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
// @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testGet()
|
||||
// @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPatch()
|
||||
if ($field_definition->getName() === 'field_rest_test_multivalue') {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
// Never ever allow this field to be viewed: this lets
|
||||
// EntityResourceTestBase::testGet() test in a "vanilla" way.
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
// @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testGet()
|
||||
// @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPatch()
|
||||
if ($field_definition->getName() === 'rest_test_validation') {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
// Never ever allow this field to be viewed: this lets
|
||||
// EntityResourceTestBase::testGet() test in a "vanilla" way.
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
// No opinion.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info().
|
||||
*/
|
||||
function rest_test_entity_base_field_info(EntityTypeInterface $entity_type) {
|
||||
$fields = [];
|
||||
$fields['rest_test_validation'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('REST test validation field'))
|
||||
->setDescription(t('A text field with some special validations attached used for testing purposes'))
|
||||
->addConstraint('rest_test_validation');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
|
|
@ -7,3 +7,12 @@ services:
|
|||
class: Drupal\rest_test\Authentication\Provider\TestAuthGlobal
|
||||
tags:
|
||||
- { name: authentication_provider, provider_id: 'rest_test_auth_global', global: TRUE }
|
||||
rest_test.page_cache_request_policy.deny_test_auth_requests:
|
||||
class: Drupal\rest_test\PageCache\RequestPolicy\DenyTestAuthRequests
|
||||
public: false
|
||||
tags:
|
||||
- { name: page_cache_request_policy }
|
||||
rest_test.encoder.foobar:
|
||||
class: Drupal\serialization\Encoder\JsonEncoder
|
||||
tags:
|
||||
- { name: encoder, format: foobar }
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest_test\PageCache\RequestPolicy;
|
||||
|
||||
use Drupal\Core\PageCache\RequestPolicyInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Cache policy for pages requested with REST Test Auth.
|
||||
*
|
||||
* This policy disallows caching of requests that use the REST Test Auth
|
||||
* authentication provider for security reasons (just like basic_auth).
|
||||
* Otherwise responses for authenticated requests can get into the page cache
|
||||
* and could be delivered to unprivileged users.
|
||||
*
|
||||
* @see \Drupal\rest_test\Authentication\Provider\TestAuth
|
||||
* @see \Drupal\rest_test\Authentication\Provider\TestAuthGlobal
|
||||
* @see \Drupal\basic_auth\PageCache\DisallowBasicAuthRequests
|
||||
*/
|
||||
class DenyTestAuthRequests implements RequestPolicyInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function check(Request $request) {
|
||||
if ($request->headers->has('REST-test-auth') || $request->headers->has('REST-test-auth-global')) {
|
||||
return self::DENY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest_test\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* Adds some validations for a REST test field.
|
||||
*
|
||||
* @Constraint(
|
||||
* id = "rest_test_validation",
|
||||
* label = @Translation("REST test validation", context = "Validation")
|
||||
* )
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\OptionsProviderInterface
|
||||
*/
|
||||
class RestTestConstraint extends Constraint {
|
||||
|
||||
public $message = 'REST test validation failed';
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest_test\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Validator for \Drupal\rest_test\Plugin\Validation\Constraint\RestTestConstraint.
|
||||
*/
|
||||
class RestTestConstraintValidator extends ConstraintValidator {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
if ($value instanceof FieldItemListInterface) {
|
||||
$value = $value->getValue();
|
||||
if (!empty($value[0]['value']) && $value[0]['value'] === 'ALWAYS_FAIL') {
|
||||
$this->context->addViolation($constraint->message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -25,7 +25,7 @@ class NoSerializationClassTestResource extends ResourceBase {
|
|||
*
|
||||
* @return \Drupal\rest\ResourceResponse
|
||||
*/
|
||||
public function post(array $data = []) {
|
||||
public function post(array $data) {
|
||||
return new ResourceResponse($data);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,5 +5,5 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- rest
|
||||
- views
|
||||
- drupal:rest
|
||||
- drupal:views
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- rest
|
||||
- user
|
||||
id: test_serializer_shared_path
|
||||
label: 'Test serializer shared path'
|
||||
module: rest
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test
|
||||
base_field: id
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
style:
|
||||
type: serializer
|
||||
row:
|
||||
type: data_entity
|
||||
sorts:
|
||||
id:
|
||||
id: standard
|
||||
table: entity_test
|
||||
field: id
|
||||
order: DESC
|
||||
plugin_id: date
|
||||
entity_type: entity_test
|
||||
entity_field: id
|
||||
title: 'Test serialize'
|
||||
arguments: { }
|
||||
rest_export_1:
|
||||
display_plugin: rest_export
|
||||
id: rest_export_1
|
||||
display_title: serializer
|
||||
position: null
|
||||
display_options:
|
||||
defaults:
|
||||
access: false
|
||||
path: test/serialize/shared
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: page
|
||||
position: null
|
||||
display_options:
|
||||
defaults:
|
||||
access: false
|
||||
style: false
|
||||
row: false
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: entity:entity_test
|
||||
path: test/serialize/shared
|
|
@ -24,13 +24,14 @@ trait AnonResourceTestTrait {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
|
||||
protected function assertResponseWhenMissingAuthentication($method, ResponseInterface $response) {
|
||||
throw new \LogicException('When testing for anonymous users, authentication cannot be missing.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {}
|
||||
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ use Psr\Http\Message\ResponseInterface;
|
|||
* authenticated, a 401 response must be sent.
|
||||
* - Because every request must send an authorization, there is no danger of
|
||||
* CSRF attacks.
|
||||
*
|
||||
* @see \Drupal\Tests\rest\Functional\BasicAuthResourceWithInterfaceTranslationTestTrait
|
||||
*/
|
||||
trait BasicAuthResourceTestTrait {
|
||||
|
||||
|
@ -31,13 +33,17 @@ trait BasicAuthResourceTestTrait {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
|
||||
$this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response);
|
||||
protected function assertResponseWhenMissingAuthentication($method, ResponseInterface $response) {
|
||||
$expected_page_cache_header_value = $method === 'GET' ? 'MISS' : FALSE;
|
||||
// @see \Drupal\basic_auth\Authentication\Provider\BasicAuth::challengeException()
|
||||
$expected_dynamic_page_cache_header_value = $expected_page_cache_header_value;
|
||||
$this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response, ['4xx-response', 'config:system.site', 'config:user.role.anonymous', 'http_response'], ['user.roles:anonymous'], $expected_page_cache_header_value, $expected_dynamic_page_cache_header_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {}
|
||||
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Trait for ResourceTestBase subclasses testing $auth=basic_auth + 'language'.
|
||||
*
|
||||
* @see \Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait
|
||||
*/
|
||||
trait BasicAuthResourceWithInterfaceTranslationTestTrait {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertResponseWhenMissingAuthentication($method, ResponseInterface $response) {
|
||||
// Because BasicAuth::challengeException() relies on the 'system.site'
|
||||
// configuration, and this test installs the 'language' module, all config
|
||||
// may be translated and therefore gets the 'languages:language_interface'
|
||||
// cache context.
|
||||
$expected_page_cache_header_value = $method === 'GET' ? 'MISS' : FALSE;
|
||||
$this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response, ['4xx-response', 'config:system.site', 'config:user.role.anonymous', 'http_response'], ['languages:language_interface', 'user.roles:anonymous'], $expected_page_cache_header_value, $expected_page_cache_header_value);
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -61,7 +61,7 @@ trait CookieResourceTestTrait {
|
|||
'pass' => $this->account->passRaw,
|
||||
];
|
||||
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($request_body, 'json');
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($request_body, static::$format);
|
||||
$request_options[RequestOptions::HEADERS] = [
|
||||
'Content-Type' => static::$mimeType,
|
||||
];
|
||||
|
@ -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'];
|
||||
}
|
||||
|
@ -91,11 +91,31 @@ trait CookieResourceTestTrait {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
|
||||
protected function assertResponseWhenMissingAuthentication($method, ResponseInterface $response) {
|
||||
// Requests needing cookie authentication but missing it results in a 403
|
||||
// response. The cookie authentication mechanism sets no response message.
|
||||
// Hence, effectively, this is just the 403 response that one gets as the
|
||||
// anonymous user trying to access a certain REST resource.
|
||||
// @see \Drupal\user\Authentication\Provider\Cookie
|
||||
// @todo https://www.drupal.org/node/2847623
|
||||
$this->assertResourceErrorResponse(403, FALSE, $response);
|
||||
if ($method === 'GET') {
|
||||
$expected_cookie_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability();
|
||||
// - \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber applies
|
||||
// to cacheable anonymous responses: it updates their cacheability.
|
||||
// - A 403 response to a GET request is cacheable.
|
||||
// Therefore we must update our cacheability expectations accordingly.
|
||||
if (in_array('user.permissions', $expected_cookie_403_cacheability->getCacheContexts(), TRUE)) {
|
||||
$expected_cookie_403_cacheability->addCacheTags(['config:user.role.anonymous']);
|
||||
}
|
||||
// @todo Fix \Drupal\block\BlockAccessControlHandler::mergeCacheabilityFromConditions() in https://www.drupal.org/node/2867881
|
||||
if (static::$entityTypeId === 'block') {
|
||||
$expected_cookie_403_cacheability->setCacheTags(str_replace('user:2', 'user:0', $expected_cookie_403_cacheability->getCacheTags()));
|
||||
}
|
||||
$this->assertResourceErrorResponse(403, FALSE, $response, $expected_cookie_403_cacheability->getCacheTags(), $expected_cookie_403_cacheability->getCacheContexts(), 'MISS', 'MISS');
|
||||
}
|
||||
else {
|
||||
$this->assertResourceErrorResponse(403, FALSE, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,23 +129,18 @@ trait CookieResourceTestTrait {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
unset($request_options[RequestOptions::HEADERS]['X-CSRF-Token']);
|
||||
|
||||
|
||||
// DX: 403 when missing X-CSRF-Token request header.
|
||||
$response = $this->request($method, $url, $request_options);
|
||||
$this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is missing', $response);
|
||||
|
||||
|
||||
$request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = 'this-is-not-the-token-you-are-looking-for';
|
||||
|
||||
|
||||
// DX: 403 when invalid X-CSRF-Token request header.
|
||||
$response = $this->request($method, $url, $request_options);
|
||||
$this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is invalid', $response);
|
||||
|
||||
|
||||
$request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Action;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ActionJsonAnonTest extends ActionResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Action;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ActionJsonBasicAuthTest extends ActionResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -2,88 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Action;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\system\Entity\Action;
|
||||
use Drupal\user\RoleInterface;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\ActionResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\system\Functional\Rest\ActionResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class ActionResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'action';
|
||||
|
||||
/**
|
||||
* @var \Drupal\system\ActionConfigEntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer actions']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$action = Action::create([
|
||||
'id' => 'user_add_role_action.' . RoleInterface::ANONYMOUS_ID,
|
||||
'type' => 'user',
|
||||
'label' => t('Add the anonymous role to the selected users'),
|
||||
'configuration' => [
|
||||
'rid' => RoleInterface::ANONYMOUS_ID,
|
||||
],
|
||||
'plugin' => 'user_add_role_action',
|
||||
]);
|
||||
$action->save();
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'configuration' => [
|
||||
'rid' => 'anonymous',
|
||||
],
|
||||
'dependencies' => [
|
||||
'config' => ['user.role.anonymous'],
|
||||
'module' => ['user'],
|
||||
],
|
||||
'id' => 'user_add_role_action.anonymous',
|
||||
'label' => 'Add the anonymous role to the selected users',
|
||||
'langcode' => 'en',
|
||||
'plugin' => 'user_add_role_action',
|
||||
'status' => TRUE,
|
||||
'type' => 'user',
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return [
|
||||
'user.permissions',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
use Drupal\Tests\system\Functional\Rest\ActionResourceTestBase as ActionResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\system\Functional\Rest\ActionResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class ActionResourceTestBase extends ActionResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BaseFieldOverride;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BaseFieldOverrideJsonAnonTest extends BaseFieldOverrideResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BaseFieldOverride;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BaseFieldOverrideJsonBasicAuthTest extends BaseFieldOverrideResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BaseFieldOverride;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BaseFieldOverrideJsonCookieTest extends BaseFieldOverrideResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,110 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BaseFieldOverride;
|
||||
|
||||
use Drupal\Core\Field\Entity\BaseFieldOverride;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\BaseFieldOverrideResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\FunctionalTests\Rest\BaseFieldOverrideResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class BaseFieldOverrideResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['field', 'node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'base_field_override';
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Field\Entity\BaseFieldOverride
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer node fields']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$camelids = NodeType::create([
|
||||
'name' => 'Camelids',
|
||||
'type' => 'camelids',
|
||||
]);
|
||||
$camelids->save();
|
||||
|
||||
$entity = BaseFieldOverride::create([
|
||||
'field_name' => 'promote',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'camelids',
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'bundle' => 'camelids',
|
||||
'default_value' => [],
|
||||
'default_value_callback' => '',
|
||||
'dependencies' => [
|
||||
'config' => [
|
||||
'node.type.camelids',
|
||||
],
|
||||
],
|
||||
'description' => '',
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'promote',
|
||||
'field_type' => 'boolean',
|
||||
'id' => 'node.camelids.promote',
|
||||
'label' => NULL,
|
||||
'langcode' => 'en',
|
||||
'required' => FALSE,
|
||||
'settings' => [
|
||||
'on_label' => 'On',
|
||||
'off_label' => 'Off',
|
||||
],
|
||||
'status' => TRUE,
|
||||
'translatable' => 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',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer node fields' permission is required.";
|
||||
}
|
||||
use Drupal\FunctionalTests\Rest\BaseFieldOverrideResourceTestBase as BaseFieldOverrideResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\FunctionalTests\Rest\BaseFieldOverrideResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class BaseFieldOverrideResourceTestBase extends BaseFieldOverrideResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BlockJsonAnonTest extends BlockResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BlockJsonCookieTest extends BlockResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,145 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\BlockResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\block\Functional\Rest\BlockResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class BlockResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'block';
|
||||
|
||||
/**
|
||||
* @var \Drupal\block\BlockInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->entity->setVisibilityConfig('user_role', [])->save();
|
||||
break;
|
||||
case 'POST':
|
||||
$this->grantPermissionsToTestedRole(['administer blocks']);
|
||||
break;
|
||||
case 'PATCH':
|
||||
$this->grantPermissionsToTestedRole(['administer blocks']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$block = Block::create([
|
||||
'plugin' => 'llama_block',
|
||||
'region' => 'header',
|
||||
'id' => 'llama',
|
||||
'theme' => 'classy',
|
||||
]);
|
||||
// All blocks can be viewed by the anonymous user by default. An interesting
|
||||
// side effect of this is that any anonymous user is also able to read the
|
||||
// corresponding block config entity via REST, even if an authentication
|
||||
// provider is configured for the block config entity REST resource! In
|
||||
// other words: Block entities do not distinguish between 'view' as in
|
||||
// "render on a page" and 'view' as in "read the configuration".
|
||||
// This prevents that.
|
||||
// @todo Fix this in https://www.drupal.org/node/2820315.
|
||||
$block->setVisibilityConfig('user_role', [
|
||||
'id' => 'user_role',
|
||||
'roles' => ['non-existing-role' => 'non-existing-role'],
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [
|
||||
'user' => '@user.current_user_context:current_user',
|
||||
],
|
||||
]);
|
||||
$block->save();
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$normalization = [
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'id' => 'llama',
|
||||
'weight' => NULL,
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'dependencies' => [
|
||||
'theme' => [
|
||||
'classy',
|
||||
],
|
||||
],
|
||||
'theme' => 'classy',
|
||||
'region' => 'header',
|
||||
'provider' => NULL,
|
||||
'plugin' => 'llama_block',
|
||||
'settings' => [
|
||||
'id' => 'broken',
|
||||
'label' => '',
|
||||
'provider' => 'core',
|
||||
'label_display' => 'visible',
|
||||
],
|
||||
'visibility' => [],
|
||||
];
|
||||
|
||||
return $normalization;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
// @see ::createEntity()
|
||||
return ['url.site'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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';
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
return "You are not authorized to view this block entity.";
|
||||
default:
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
}
|
||||
use Drupal\Tests\block\Functional\Rest\BlockResourceTestBase as BlockResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\block\Functional\Rest\BlockResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class BlockResourceTestBase extends BlockResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BlockContent;
|
||||
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\BlockContentResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\block_content\Functional\Rest\BlockContentResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Tests\block_content\Functional\Rest\BlockContentResourceTestBase as BlockContentResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\block_content\Functional\Rest\BlockContentResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class BlockContentResourceTestBase extends BlockContentResourceTestBaseReal {
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BlockContentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BlockContentTypeJsonAnonTest extends BlockContentTypeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BlockContentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BlockContentTypeJsonBasicAuthTest extends BlockContentTypeResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BlockContentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class BlockContentTypeJsonCookieTest extends BlockContentTypeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,70 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\BlockContentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\BlockContentTypeResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\block_content\Functional\Rest\BlockContentTypeResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class BlockContentTypeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block_content'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'block_content_type';
|
||||
|
||||
/**
|
||||
* @var \Drupal\block_content\Entity\BlockContentTypeInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer blocks']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$block_content_type = BlockContentType::create([
|
||||
'id' => 'pascal',
|
||||
'label' => 'Pascal',
|
||||
'revision' => FALSE,
|
||||
'description' => 'Provides a competitive alternative to the "basic" type',
|
||||
]);
|
||||
|
||||
$block_content_type->save();
|
||||
|
||||
return $block_content_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'description' => 'Provides a competitive alternative to the "basic" type',
|
||||
'id' => 'pascal',
|
||||
'label' => 'Pascal',
|
||||
'langcode' => 'en',
|
||||
'revision' => 0,
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
use Drupal\Tests\block_content\Functional\Rest\BlockContentTypeResourceTestBase as BlockContentTypeResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\block_content\Functional\Rest\BlockContentTypeResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class BlockContentTypeResourceTestBase extends BlockContentTypeResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class CommentJsonAnonTest extends CommentResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Anononymous users cannot edit their own comments.
|
||||
*
|
||||
* @see \Drupal\comment\CommentAccessControlHandler::checkAccess
|
||||
*
|
||||
* Therefore we grant them the 'administer comments' permission for the
|
||||
* purpose of this test.
|
||||
*
|
||||
* @see ::setUpAuthorization
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'pid',
|
||||
'entity_id',
|
||||
'changed',
|
||||
'thread',
|
||||
'entity_type',
|
||||
'field_name',
|
||||
];
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class CommentJsonBasicAuthTest extends CommentResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class CommentJsonCookieTest extends CommentResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,330 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
|
||||
|
||||
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\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\CommentResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\comment\Functional\Rest\CommentResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class CommentResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['comment', 'entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'comment';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'status',
|
||||
'pid',
|
||||
'entity_id',
|
||||
'uid',
|
||||
'name',
|
||||
'homepage',
|
||||
'created',
|
||||
'changed',
|
||||
'thread',
|
||||
'entity_type',
|
||||
'field_name',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Drupal\comment\CommentInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['access comments', 'view test entity']);
|
||||
break;
|
||||
case 'POST':
|
||||
$this->grantPermissionsToTestedRole(['post comments']);
|
||||
break;
|
||||
case 'PATCH':
|
||||
// Anononymous users are not ever allowed to edit their own comments. To
|
||||
// be able to test PATCHing comments as the anonymous user, the more
|
||||
// permissive 'administer comments' permission must be granted.
|
||||
// @see \Drupal\comment\CommentAccessControlHandler::checkAccess
|
||||
if (static::$auth) {
|
||||
$this->grantPermissionsToTestedRole(['edit own comments']);
|
||||
}
|
||||
else {
|
||||
$this->grantPermissionsToTestedRole(['administer comments']);
|
||||
}
|
||||
break;
|
||||
case 'DELETE':
|
||||
$this->grantPermissionsToTestedRole(['administer comments']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "bar" bundle for the "entity_test" entity type and create.
|
||||
$bundle = 'bar';
|
||||
entity_test_create_bundle($bundle, NULL, 'entity_test');
|
||||
|
||||
// Create a comment field on this bundle.
|
||||
$this->addDefaultCommentField('entity_test', 'bar', 'comment');
|
||||
|
||||
// Create a "Camelids" test entity that the comment will be assigned to.
|
||||
$commented_entity = EntityTest::create([
|
||||
'name' => 'Camelids',
|
||||
'type' => 'bar',
|
||||
]);
|
||||
$commented_entity->save();
|
||||
|
||||
// Create a "Llama" comment.
|
||||
$comment = Comment::create([
|
||||
'comment_body' => [
|
||||
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
'entity_id' => $commented_entity->id(),
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'comment',
|
||||
]);
|
||||
$comment->setSubject('Llama')
|
||||
->setOwnerId(static::$auth ? $this->account->id() : 0)
|
||||
->setPublished(TRUE)
|
||||
->setCreatedTime(123456789)
|
||||
->setChangedTime(123456789);
|
||||
$comment->save();
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$author = User::load($this->entity->getOwnerId());
|
||||
return [
|
||||
'cid' => [
|
||||
['value' => 1],
|
||||
],
|
||||
'uuid' => [
|
||||
['value' => $this->entity->uuid()],
|
||||
],
|
||||
'langcode' => [
|
||||
[
|
||||
'value' => 'en',
|
||||
],
|
||||
],
|
||||
'comment_type' => [
|
||||
[
|
||||
'target_id' => 'comment',
|
||||
'target_type' => 'comment_type',
|
||||
'target_uuid' => CommentType::load('comment')->uuid(),
|
||||
],
|
||||
],
|
||||
'subject' => [
|
||||
[
|
||||
'value' => 'Llama',
|
||||
],
|
||||
],
|
||||
'status' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => 123456789,
|
||||
],
|
||||
],
|
||||
'changed' => [
|
||||
[
|
||||
'value' => $this->entity->getChangedTime(),
|
||||
],
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'uid' => [
|
||||
[
|
||||
'target_id' => (int) $author->id(),
|
||||
'target_type' => 'user',
|
||||
'target_uuid' => $author->uuid(),
|
||||
'url' => base_path() . 'user/' . $author->id(),
|
||||
],
|
||||
],
|
||||
'pid' => [],
|
||||
'entity_type' => [
|
||||
[
|
||||
'value' => 'entity_test',
|
||||
],
|
||||
],
|
||||
'entity_id' => [
|
||||
[
|
||||
'target_id' => 1,
|
||||
'target_type' => 'entity_test',
|
||||
'target_uuid' => EntityTest::load(1)->uuid(),
|
||||
'url' => base_path() . 'entity_test/1',
|
||||
],
|
||||
],
|
||||
'field_name' => [
|
||||
[
|
||||
'value' => 'comment',
|
||||
],
|
||||
],
|
||||
'name' => [],
|
||||
'homepage' => [],
|
||||
'thread' => [
|
||||
[
|
||||
'value' => '01/',
|
||||
],
|
||||
],
|
||||
'comment_body' => [
|
||||
[
|
||||
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'comment_type' => [
|
||||
[
|
||||
'target_id' => 'comment',
|
||||
],
|
||||
],
|
||||
'entity_type' => [
|
||||
[
|
||||
'value' => 'entity_test',
|
||||
],
|
||||
],
|
||||
'entity_id' => [
|
||||
[
|
||||
'target_id' => EntityTest::load(1)->id(),
|
||||
],
|
||||
],
|
||||
'field_name' => [
|
||||
[
|
||||
'value' => 'comment',
|
||||
],
|
||||
],
|
||||
'subject' => [
|
||||
[
|
||||
'value' => 'Dramallama',
|
||||
],
|
||||
],
|
||||
'comment_body' => [
|
||||
[
|
||||
'value' => 'Llamas are awesome.',
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPatchEntity() {
|
||||
return array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE, 'entity_id' => TRUE, 'field_name' => TRUE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests POSTing a comment without critical base fields.
|
||||
*
|
||||
* testPost() is testing with the most minimal normalization possible: the one
|
||||
* returned by ::getNormalizedPostEntity().
|
||||
*
|
||||
* But Comment entities have some very special edge cases:
|
||||
* - base fields that are not marked as required in
|
||||
* \Drupal\comment\Entity\Comment::baseFieldDefinitions() yet in fact are
|
||||
* required.
|
||||
* - base fields that are marked as required, but yet can still result in
|
||||
* validation errors other than "missing required field".
|
||||
*/
|
||||
public function testPostDxWithoutCriticalBaseFields() {
|
||||
$this->initAuthentication();
|
||||
$this->provisionEntityResource();
|
||||
$this->setUpAuthorization('POST');
|
||||
|
||||
$url = $this->getEntityResourcePostUrl()->setOption('query', ['_format' => static::$format]);
|
||||
$request_options = [];
|
||||
$request_options[RequestOptions::HEADERS]['Accept'] = static::$mimeType;
|
||||
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('POST'));
|
||||
|
||||
// DX: 422 when missing 'entity_type' field.
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE]), static::$format);
|
||||
$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);
|
||||
|
||||
// DX: 422 when missing 'entity_id' field.
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_id' => TRUE]), static::$format);
|
||||
// @todo Remove the try/catch in favor of the two commented lines in
|
||||
// https://www.drupal.org/node/2820364.
|
||||
try {
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
// This happens on DrupalCI.
|
||||
//$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());
|
||||
}
|
||||
//$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.
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 'access comments' permission is required and the comment must be published.";
|
||||
case 'POST';
|
||||
return "The 'post comments' permission is required.";
|
||||
default:
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
}
|
||||
use Drupal\Tests\comment\Functional\Rest\CommentResourceTestBase as CommentResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\comment\Functional\Rest\CommentResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class CommentResourceTestBase extends CommentResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\CommentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class CommentTypeJsonAnonTest extends CommentTypeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\CommentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class CommentTypeJsonBasicAuthTest extends CommentTypeResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\CommentType;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class CommentTypeJsonCookieTest extends CommentTypeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,76 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\CommentType;
|
||||
|
||||
use Drupal\comment\Entity\CommentType;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\CommentTypeResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\comment\Functional\Rest\CommentTypeResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Tests\comment\Functional\Rest\CommentTypeResourceTestBase as CommentTypeResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for CommentType entity.
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\comment\Functional\Rest\CommentTypeResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class CommentTypeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'comment'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'comment_type';
|
||||
|
||||
/**
|
||||
* The CommentType entity.
|
||||
*
|
||||
* @var \Drupal\comment\CommentTypeInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer comment types']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" comment type.
|
||||
$camelids = CommentType::create([
|
||||
'id' => 'camelids',
|
||||
'label' => 'Camelids',
|
||||
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
|
||||
'target_entity_type_id' => 'node',
|
||||
]);
|
||||
|
||||
$camelids->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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,
|
||||
'target_entity_type_id' => 'node',
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
abstract class CommentTypeResourceTestBase extends CommentTypeResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestJsonBasicAuthTest extends ConfigTestResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestJsonCookieTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,72 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
|
||||
|
||||
use Drupal\config_test\Entity\ConfigTest;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\ConfigTestResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\config_test\Functional\Rest\ConfigTestResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class ConfigTestResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['config_test', 'config_test_rest'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'config_test';
|
||||
|
||||
/**
|
||||
* @var \Drupal\config_test\ConfigTestInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['view config_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$config_test = ConfigTest::create([
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
]);
|
||||
$config_test->save();
|
||||
|
||||
return $config_test;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$normalization = [
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'id' => 'llama',
|
||||
'weight' => 0,
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'dependencies' => [],
|
||||
'label' => 'Llama',
|
||||
'style' => NULL,
|
||||
'size' => NULL,
|
||||
'size_value' => NULL,
|
||||
'protected_property' => NULL,
|
||||
];
|
||||
|
||||
return $normalization;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
use Drupal\Tests\config_test\Functional\Rest\ConfigTestResourceTestBase as ConfigTestResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\config_test\Functional\Rest\ConfigTestResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class ConfigTestResourceTestBase extends ConfigTestResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigurableLanguage;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigurableLanguageJsonAnonTest extends ConfigurableLanguageResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigurableLanguage;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigurableLanguageJsonBasicAuthTest extends ConfigurableLanguageResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigurableLanguage;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigurableLanguageJsonCookieTest extends ConfigurableLanguageResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,76 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigurableLanguage;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\ConfigurableLanguageResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\language\Functional\Rest\ConfigurableLanguageResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class ConfigurableLanguageResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'configurable_language';
|
||||
|
||||
/**
|
||||
* @var \Drupal\language\ConfigurableLanguageInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer languages']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$configurable_language = ConfigurableLanguage::create([
|
||||
'id' => 'll',
|
||||
'label' => 'Llama Language',
|
||||
]);
|
||||
$configurable_language->save();
|
||||
|
||||
return $configurable_language;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'direction' => 'ltr',
|
||||
'id' => 'll',
|
||||
'label' => 'Llama Language',
|
||||
'langcode' => 'en',
|
||||
'locked' => FALSE,
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'weight' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['languages:language_interface']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
use Drupal\Tests\language\Functional\Rest\ConfigurableLanguageResourceTestBase as ConfigurableLanguageResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\language\Functional\Rest\ConfigurableLanguageResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class ConfigurableLanguageResourceTestBase extends ConfigurableLanguageResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
|
||||
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\ContactFormResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\contact\Functional\Rest\ContactFormResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Tests\contact\Functional\Rest\ContactFormResourceTestBase as ContactFormResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\contact\Functional\Rest\ContactFormResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class ContactFormResourceTestBase extends ContactFormResourceTestBaseReal {
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContentLanguageSettings;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContentLanguageSettingsJsonAnonTest extends ContentLanguageSettingsResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContentLanguageSettings;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContentLanguageSettingsJsonBasicAuthTest extends ContentLanguageSettingsResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContentLanguageSettings;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContentLanguageSettingsJsonCookieTest extends ContentLanguageSettingsResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,91 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\ContentLanguageSettings;
|
||||
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\ContentLanguageSettingsResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\language\Functional\Rest\ContentLanguageSettingsResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class ContentLanguageSettingsResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'language_content_settings';
|
||||
|
||||
/**
|
||||
* @var \Drupal\language\ContentLanguageSettingsInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer languages']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" node type.
|
||||
$camelids = NodeType::create([
|
||||
'name' => 'Camelids',
|
||||
'type' => 'camelids',
|
||||
]);
|
||||
$camelids->save();
|
||||
|
||||
$entity = ContentLanguageSettings::create([
|
||||
'target_entity_type_id' => 'node',
|
||||
'target_bundle' => 'camelids',
|
||||
]);
|
||||
$entity->setDefaultLangcode('site_default')
|
||||
->save();
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'default_langcode' => 'site_default',
|
||||
'dependencies' => [
|
||||
'config' => [
|
||||
'node.type.camelids',
|
||||
],
|
||||
],
|
||||
'id' => 'node.camelids',
|
||||
'langcode' => 'en',
|
||||
'language_alterable' => FALSE,
|
||||
'status' => TRUE,
|
||||
'target_bundle' => 'camelids',
|
||||
'target_entity_type_id' => 'node',
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return [
|
||||
'languages:language_interface',
|
||||
'user.permissions',
|
||||
];
|
||||
}
|
||||
use Drupal\Tests\language\Functional\Rest\ContentLanguageSettingsResourceTestBase as ContentLanguageSettingsResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\language\Functional\Rest\ContentLanguageSettingsResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class ContentLanguageSettingsResourceTestBase extends ContentLanguageSettingsResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
|
||||
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\DateFormatResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\FunctionalTests\Rest\DateFormatResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\FunctionalTests\Rest\DateFormatResourceTestBase as DateFormatResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\FunctionalTests\Rest\DateFormatResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class DateFormatResourceTestBase extends DateFormatResourceTestBaseReal {
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Editor;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Editor;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Editor;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,183 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\Editor;
|
||||
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\EditorResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase as EditorResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for Editor entity.
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class EditorResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ckeditor', 'editor'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'editor';
|
||||
|
||||
/**
|
||||
* The Editor entity.
|
||||
*
|
||||
* @var \Drupal\editor\EditorInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer filters']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Llama" filter format.
|
||||
$llama_format = FilterFormat::create([
|
||||
'name' => 'Llama',
|
||||
'format' => 'llama',
|
||||
'langcode' => 'es',
|
||||
'filters' => [
|
||||
'filter_html' => [
|
||||
'status' => TRUE,
|
||||
'settings' => [
|
||||
'allowed_html' => '<p> <a> <b> <lo>',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$llama_format->save();
|
||||
|
||||
// Create a "Camelids" editor.
|
||||
$camelids = Editor::create([
|
||||
'format' => 'llama',
|
||||
'editor' => 'ckeditor',
|
||||
]);
|
||||
$camelids
|
||||
->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'scheme' => file_default_scheme(),
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => '',
|
||||
'height' => '',
|
||||
],
|
||||
])
|
||||
->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [
|
||||
'config' => [
|
||||
'filter.format.llama',
|
||||
],
|
||||
'module' => [
|
||||
'ckeditor',
|
||||
],
|
||||
],
|
||||
'editor' => 'ckeditor',
|
||||
'format' => 'llama',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
],
|
||||
'langcode' => 'en',
|
||||
'settings' => [
|
||||
'toolbar' => [
|
||||
'rows' => [
|
||||
[
|
||||
[
|
||||
'name' => 'Formatting',
|
||||
'items' => [
|
||||
'Bold',
|
||||
'Italic',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Links',
|
||||
'items' => [
|
||||
'DrupalLink',
|
||||
'DrupalUnlink',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Lists',
|
||||
'items' => [
|
||||
'BulletedList',
|
||||
'NumberedList',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Media',
|
||||
'items' => [
|
||||
'Blockquote',
|
||||
'DrupalImage',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Tools',
|
||||
'items' => [
|
||||
'Source',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'plugins' => [
|
||||
'language' => [
|
||||
'language_list' => 'un',
|
||||
],
|
||||
],
|
||||
],
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
// @see ::createEntity()
|
||||
return ['user.permissions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer filters' permission is required.";
|
||||
}
|
||||
|
||||
abstract class EditorResourceTestBase extends EditorResourceTestBaseReal {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormDisplay;
|
||||
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\EntityFormDisplayResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\FunctionalTests\Rest\EntityFormDisplayResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\FunctionalTests\Rest\EntityFormDisplayResourceTestBase as EntityFormDisplayResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\FunctionalTests\Rest\EntityFormDisplayResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class EntityFormDisplayResourceTestBase extends EntityFormDisplayResourceTestBaseReal {
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
|
||||
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\EntityFormModeResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\FunctionalTests\Rest\EntityFormModeResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\FunctionalTests\Rest\EntityFormModeResourceTestBase as EntityFormModeResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\FunctionalTests\Rest\EntityFormModeResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class EntityFormModeResourceTestBase extends EntityFormModeResourceTestBaseReal {
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Checks that all core content/config entity types have REST test coverage.
|
||||
*
|
||||
* Every entity type must have test coverage for:
|
||||
* - every format in core (json + xml + hal_json)
|
||||
* - every authentication provider in core (anon, cookie, basic_auth)
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class EntityResourceRestTestCoverageTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Entity definitions array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $definitions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$all_modules = system_rebuild_module_data();
|
||||
$stable_core_modules = array_filter($all_modules, function ($module) {
|
||||
// Filter out contrib, hidden, testing, and experimental modules. We also
|
||||
// don't need to enable modules that are already enabled.
|
||||
return
|
||||
$module->origin === 'core' &&
|
||||
empty($module->info['hidden']) &&
|
||||
$module->status == FALSE &&
|
||||
$module->info['package'] !== 'Testing' &&
|
||||
$module->info['package'] !== 'Core (Experimental)';
|
||||
});
|
||||
|
||||
$this->container->get('module_installer')->install(array_keys($stable_core_modules));
|
||||
$this->rebuildContainer();
|
||||
|
||||
$this->definitions = $this->container->get('entity_type.manager')->getDefinitions();
|
||||
|
||||
// Entity types marked as "internal" are not exposed by the entity REST
|
||||
// resource plugin and hence also don't need test coverage.
|
||||
$this->definitions = array_filter($this->definitions, function (EntityTypeInterface $entity_type) {
|
||||
return !$entity_type->isInternal();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that all core content/config entity types have REST test coverage.
|
||||
*/
|
||||
public function testEntityTypeRestTestCoverage() {
|
||||
$tests = [
|
||||
// Test coverage for formats provided by the 'serialization' module.
|
||||
'serialization' => [
|
||||
'path' => '\Drupal\Tests\PROVIDER\Functional\Rest\CLASS',
|
||||
'class suffix' => [
|
||||
'JsonAnonTest',
|
||||
'JsonBasicAuthTest',
|
||||
'JsonCookieTest',
|
||||
'XmlAnonTest',
|
||||
'XmlBasicAuthTest',
|
||||
'XmlCookieTest',
|
||||
],
|
||||
],
|
||||
// Test coverage for formats provided by the 'hal' module.
|
||||
'hal' => [
|
||||
'path' => '\Drupal\Tests\PROVIDER\Functional\Hal\CLASS',
|
||||
'class suffix' => [
|
||||
'HalJsonAnonTest',
|
||||
'HalJsonBasicAuthTest',
|
||||
'HalJsonCookieTest',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$problems = [];
|
||||
foreach ($this->definitions as $entity_type_id => $info) {
|
||||
$class_name_full = $info->getClass();
|
||||
$parts = explode('\\', $class_name_full);
|
||||
$class_name = end($parts);
|
||||
$module_name = $parts[1];
|
||||
|
||||
foreach ($tests as $module => $info) {
|
||||
$path = $info['path'];
|
||||
$missing_tests = [];
|
||||
foreach ($info['class suffix'] as $postfix) {
|
||||
$class = str_replace(['PROVIDER', 'CLASS'], [$module_name, $class_name], $path . $postfix);
|
||||
$class_alternative = str_replace("\\Drupal\\Tests\\$module_name\\Functional", '\Drupal\FunctionalTests', $class);
|
||||
if (class_exists($class) || class_exists($class_alternative)) {
|
||||
continue;
|
||||
}
|
||||
$missing_tests[] = $postfix;
|
||||
}
|
||||
if (!empty($missing_tests)) {
|
||||
$missing_tests_list = implode(', ', array_map(function ($missing_test) use ($class_name) {
|
||||
return $class_name . $missing_test;
|
||||
}, $missing_tests));
|
||||
$which_normalization = $module === 'serialization' ? 'default' : $module;
|
||||
$problems[] = "$entity_type_id: $class_name ($class_name_full), $which_normalization normalization (expected tests: $missing_tests_list)";
|
||||
}
|
||||
}
|
||||
}
|
||||
$all = count($this->definitions);
|
||||
$good = $all - count($problems);
|
||||
$this->assertSame([], $problems, $this->getLlamaMessage($good, $all));
|
||||
}
|
||||
|
||||
/**
|
||||
* Message from Llama.
|
||||
*
|
||||
* @param int $g
|
||||
* A count of entities with test coverage.
|
||||
* @param int $a
|
||||
* A count of all entities.
|
||||
*
|
||||
* @return string
|
||||
* An information about progress of REST test coverage.
|
||||
*/
|
||||
protected function getLlamaMessage($g, $a) {
|
||||
return "
|
||||
☼
|
||||
________________________
|
||||
/ Hi! \\
|
||||
| It's llame to not have |
|
||||
| complete REST tests! |
|
||||
| |
|
||||
| Progress: $g/$a. |
|
||||
| ________________________/
|
||||
|/
|
||||
// o
|
||||
l'>
|
||||
ll
|
||||
llama
|
||||
|| ||
|
||||
'' ''
|
||||
";
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityTestJsonAnonTest extends EntityTestResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityTestJsonBasicAuthTest extends EntityTestResourceTestBase {
|
||||
|
||||
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';
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EntityTestJsonCookieTest extends EntityTestResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -2,144 +2,15 @@
|
|||
|
||||
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\EntityTestResourceTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\entity_test\Functional\Rest\EntityTestResourceTestBase. See https://www.drupal.org/node/2971931.', E_USER_DEPRECATED);
|
||||
|
||||
abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'entity_test';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [];
|
||||
|
||||
/**
|
||||
* @var \Drupal\entity_test\Entity\EntityTest
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['view test entity']);
|
||||
break;
|
||||
case 'POST':
|
||||
$this->grantPermissionsToTestedRole(['create entity_test entity_test_with_bundle entities']);
|
||||
break;
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$this->grantPermissionsToTestedRole(['administer entity_test content']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$entity_test = EntityTest::create([
|
||||
'name' => 'Llama',
|
||||
'type' => 'entity_test',
|
||||
]);
|
||||
$entity_test->setOwnerId(0);
|
||||
$entity_test->save();
|
||||
|
||||
return $entity_test;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$author = User::load(0);
|
||||
$normalization = [
|
||||
'uuid' => [
|
||||
[
|
||||
'value' => $this->entity->uuid()
|
||||
]
|
||||
],
|
||||
'id' => [
|
||||
[
|
||||
'value' => 1,
|
||||
],
|
||||
],
|
||||
'langcode' => [
|
||||
[
|
||||
'value' => 'en',
|
||||
],
|
||||
],
|
||||
'type' => [
|
||||
[
|
||||
'value' => 'entity_test',
|
||||
]
|
||||
],
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Llama',
|
||||
]
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => (int) $this->entity->get('created')->value,
|
||||
]
|
||||
],
|
||||
'user_id' => [
|
||||
[
|
||||
'target_id' => (int) $author->id(),
|
||||
'target_type' => 'user',
|
||||
'target_uuid' => $author->uuid(),
|
||||
'url' => $author->toUrl()->toString(),
|
||||
]
|
||||
],
|
||||
'field_test_text' => [],
|
||||
];
|
||||
|
||||
return $normalization;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'type' => 'entity_test',
|
||||
'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 test entity' permission is required.";
|
||||
case 'POST':
|
||||
return "The following permissions are required: 'administer entity_test content' OR 'administer entity_test_with_bundle content' OR 'create entity_test entity_test_with_bundle entities'.";
|
||||
default:
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
}
|
||||
use Drupal\Tests\entity_test\Functional\Rest\EntityTestResourceTestBase as EntityTestResourceTestBaseReal;
|
||||
|
||||
/**
|
||||
* @deprecated in Drupal 8.6.x. Will be removed before Drupal 9.0.0. Use
|
||||
* Drupal\Tests\entity_test\Functional\Rest\EntityTestResourceTestBase instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2971931
|
||||
*/
|
||||
abstract class EntityTestResourceTestBase extends EntityTestResourceTestBaseReal {
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue