diff --git a/core/modules/rest/config/optional/rest.endpoint.entity__node.yml b/core/modules/rest/config/optional/rest.endpoint.entity__node.yml new file mode 100644 index 0000000..e8c73d5 --- /dev/null +++ b/core/modules/rest/config/optional/rest.endpoint.entity__node.yml @@ -0,0 +1,28 @@ +id: entity__node +plugin_id: "entity:node" +configuration: + GET: + supported_formats: + - hal_json + supported_auth: + - basic_auth + POST: + supported_formats: + - hal_json + supported_auth: + - basic_auth + PATCH: + supported_formats: + - hal_json + supported_auth: + - basic_auth + DELETE: + supported_formats: + - hal_json + supported_auth: + - basic_auth +dependencies: + module: + - node + - basic_auth + - hal diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml index 8a6ea43..4a9770f 100644 --- a/core/modules/rest/config/schema/rest.schema.yml +++ b/core/modules/rest/config/schema/rest.schema.yml @@ -47,6 +47,9 @@ rest.endpoint.*: type: config_entity label: 'REST endpooint' mapping: + id: + type: string + label: 'REST endpoint ID' plugin_id: type: string label: 'REST endpoint plugin id' diff --git a/core/modules/rest/rest.api.php b/core/modules/rest/rest.api.php index 1ce2251..2f3f6ee 100644 --- a/core/modules/rest/rest.api.php +++ b/core/modules/rest/rest.api.php @@ -17,15 +17,15 @@ * The collection of resource definitions. */ function hook_rest_resource_alter(&$definitions) { - if (isset($definitions['entity__node'])) { + if (isset($definitions['entity:node'])) { // We want to handle REST requests regarding nodes with our own plugin // class. - $definitions['entity__node']['class'] = 'Drupal\mymodule\Plugin\rest\resource\NodeResource'; + $definitions['entity:node']['class'] = 'Drupal\mymodule\Plugin\rest\resource\NodeResource'; // Serialized nodes should be expanded to my specific node class. - $definitions['entity__node']['serialization_class'] = 'Drupal\mymodule\Entity\MyNode'; + $definitions['entity:node']['serialization_class'] = 'Drupal\mymodule\Entity\MyNode'; } // We don't want Views to show up in the array of plugins at all. - unset($definitions['entity__view']); + unset($definitions['entity:view']); } /** diff --git a/core/modules/rest/src/Entity/RestEndpoint.php b/core/modules/rest/src/Entity/RestEndpoint.php index ae4a4ac..df756b6 100644 --- a/core/modules/rest/src/Entity/RestEndpoint.php +++ b/core/modules/rest/src/Entity/RestEndpoint.php @@ -3,6 +3,7 @@ namespace Drupal\rest\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Entity\Entity; use Drupal\rest\RestEndpointInterface; /** @@ -15,12 +16,19 @@ * admin_permission = "administer rest endpoints", * label_callback = "getLabelFromPlugin", * entity_keys = { - * "id" = "plugin_id" + * "id" = "id" * } * ) */ class RestEndpoint extends ConfigEntityBase implements RestEndpointInterface { /** + * The REST endpoint ID. + * + * @var string + */ + protected $id; + + /** * The REST endpoint plugin id. * * @var string @@ -34,22 +42,35 @@ class RestEndpoint extends ConfigEntityBase implements RestEndpointInterface { */ protected $configuration; - /** - * {@inheritdoc} - */ - public function id() { - return isset($this->plugin_id) ? $this->plugin_id : NULL; + public function __construct(array $values, $entity_type) { + parent::__construct($values, $entity_type); + // The config entity id looks like the plugin id but uses _ instead of : because : is not valid for config entities. + if (!isset($this->plugin_id) && isset($this->id)) { + $this->plugin_id = str_replace('__', ':', $this->id); + } } + /** + * The label callback for this configuration entity. + * + * @return string The label + */ protected function getLabelFromPlugin() { $plugin_definition = \Drupal::service('plugin.manager.rest') - ->getDefinition(['id' => $this->plugin_id]); + ->getDefinition(['id' => $this->getPluginID()]); return $plugin_definition['label']; } /** * {@inheritdoc} */ + public function getPluginID() { + return $this->plugin_id; + } + + /** + * {@inheritdoc} + */ public function getSettings() { return $this->configuration; } diff --git a/core/modules/rest/src/Plugin/Deriver/EntityDeriver.php b/core/modules/rest/src/Plugin/Deriver/EntityDeriver.php index 59db920..3d987a2 100644 --- a/core/modules/rest/src/Plugin/Deriver/EntityDeriver.php +++ b/core/modules/rest/src/Plugin/Deriver/EntityDeriver.php @@ -74,7 +74,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { // Add in the default plugin configuration and the resource type. foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { $this->derivatives[$entity_type_id] = array( - 'id' => 'entity__' . $entity_type_id, + 'id' => 'entity:' . $entity_type_id, 'entity_type' => $entity_type_id, 'serialization_class' => $entity_type->getClass(), 'label' => $entity_type->getLabel(), diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php index fac6eaf..70d6268 100644 --- a/core/modules/rest/src/Plugin/ResourceBase.php +++ b/core/modules/rest/src/Plugin/ResourceBase.php @@ -78,7 +78,7 @@ public static function create(ContainerInterface $container, array $configuratio * Implements ResourceInterface::permissions(). * * Every plugin operation method gets its own user permission. Example: - * "restful delete entity__node" with the title "Access DELETE on Node + * "restful delete entity:node" with the title "Access DELETE on Node * resource". */ public function permissions() { diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php index 73691ce..2c5eea9 100644 --- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php @@ -226,7 +226,7 @@ protected function getBaseRoute($canonical_path, $method) { $definition = $this->getPluginDefinition(); $parameters = $route->getOption('parameters') ?: array(); - $parameters[$definition['entity_type']]['type'] = 'entity__' . $definition['entity_type']; + $parameters[$definition['entity_type']]['type'] = 'entity:' . $definition['entity_type']; $route->setOption('parameters', $parameters); return $route; diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php index 4079b2f..121f32c 100644 --- a/core/modules/rest/src/RequestHandler.php +++ b/core/modules/rest/src/RequestHandler.php @@ -48,7 +48,8 @@ public function handle(RouteMatchInterface $route_match, Request $request) { ->getInstance(array('id' => $plugin)); /** @var \Drupal\rest\RestEndpointInterface $endpoint */ - $endpoint = RestEndpoint::load($plugin); + $endpoint_id = str_replace(':', '__', $plugin); + $endpoint = RestEndpoint::load($endpoint_id); // Deserialize incoming data if available. $serializer = $this->container->get('serializer'); diff --git a/core/modules/rest/src/RestEndpointInterface.php b/core/modules/rest/src/RestEndpointInterface.php index eb141d6..c2a0fb9 100644 --- a/core/modules/rest/src/RestEndpointInterface.php +++ b/core/modules/rest/src/RestEndpointInterface.php @@ -14,4 +14,9 @@ public function getSettings(); * @param array $settings */ public function setSettings(array $settings); + + /** + * @return string The plugin id of the resource for this REST endpoint. + */ + public function getPluginID(); } diff --git a/core/modules/rest/src/RestPermissions.php b/core/modules/rest/src/RestPermissions.php index b85db2c..d7e1cf1 100644 --- a/core/modules/rest/src/RestPermissions.php +++ b/core/modules/rest/src/RestPermissions.php @@ -62,7 +62,7 @@ public function permissions() { $endpoints = $this->endpoint_storage->loadMultiple(); foreach ($endpoints as $endpoint) { if ($this->restPluginManager->getDefinition($endpoint->id(), FALSE)) { - $plugin = $this->restPluginManager->getInstance(['id' => $endpoint->id()]); + $plugin = $this->restPluginManager->getInstance(['id' => $endpoint->getPluginID()]); $permissions = array_merge($permissions, $plugin->permissions()); } } diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php index e932c55..31b6a84 100644 --- a/core/modules/rest/src/Routing/ResourceRoutes.php +++ b/core/modules/rest/src/Routing/ResourceRoutes.php @@ -74,7 +74,7 @@ protected function alterRoutes(RouteCollection $collection) { /** @var \Drupal\rest\RestEndpointInterface[] $endpoints */ $endpoints = $this->endpoint_storage->loadMultiple(); foreach ($endpoints as $endpoint) { - $plugin = $this->manager->getInstance(array('id' => $endpoint->id())); + $plugin = $this->manager->getInstance(array('id' => $endpoint->getPluginID())); $enabled_methods = $endpoint->getSettings(); foreach ($plugin->routes() as $name => $route) { diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php index 6a7aa2c..1919aa6 100644 --- a/core/modules/rest/src/Tests/AuthTest.php +++ b/core/modules/rest/src/Tests/AuthTest.php @@ -31,7 +31,7 @@ public function testRead() { $entity_type = 'entity_test'; // Enable a test resource through GET method and basic HTTP authentication. - $this->enableService('entity__' . $entity_type, 'GET', NULL, array('basic_auth')); + $this->enableService('entity:' . $entity_type, 'GET', NULL, array('basic_auth')); // Create an entity programmatically. $entity = $this->entityCreate($entity_type); @@ -49,7 +49,7 @@ public function testRead() { // resources via the REST API, but the request is authenticated // with session cookies. $permissions = $this->entityPermissions($entity_type, 'view'); - $permissions[] = 'restful get entity__' . $entity_type; + $permissions[] = 'restful get entity:' . $entity_type; $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php index 8fee56e..1d143ab 100644 --- a/core/modules/rest/src/Tests/CreateTest.php +++ b/core/modules/rest/src/Tests/CreateTest.php @@ -46,12 +46,12 @@ protected function setUp() { public function testCreateResourceRestApiNotEnabled() { $entity_type = 'entity_test'; // Enables the REST service for a specific entity type. - $this->enableService('entity__' . $entity_type, 'POST'); + $this->enableService('entity:' . $entity_type, 'POST'); // Get the necessary user permissions to create the current entity type. $permissions = $this->entityPermissions($entity_type, 'create'); // POST method must be allowed for the current entity type. - $permissions[] = 'restful post entity__' . $entity_type; + $permissions[] = 'restful post entity:' . $entity_type; // Create the user. $account = $this->drupalCreateUser($permissions); @@ -81,9 +81,9 @@ public function testCreateResourceRestApiNotEnabled() { public function testCreateWithoutPermission() { $entity_type = 'entity_test'; // Enables the REST service for 'entity_test' entity type. - $this->enableService('entity__' . $entity_type, 'POST'); + $this->enableService('entity:' . $entity_type, 'POST'); $permissions = $this->entityPermissions($entity_type, 'create'); - // Create a user without the 'restful post entity__entity_test permission. + // Create a user without the 'restful post entity:entity_test permission. $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); // Populate some entity properties before create the entity. @@ -105,7 +105,7 @@ public function testCreateWithoutPermission() { public function testCreateEntityTest() { $entity_type = 'entity_test'; // Enables the REST service for 'entity_test' entity type. - $this->enableService('entity__' . $entity_type, 'POST'); + $this->enableService('entity:' . $entity_type, 'POST'); // Create two accounts with the required permissions to create resources. // The second one has administrative permissions. $accounts = $this->createAccountPerEntity($entity_type); @@ -172,7 +172,7 @@ public function testCreateEntityTest() { public function testCreateNode() { $entity_type = 'node'; // Enables the REST service for 'node' entity type. - $this->enableService('entity__' . $entity_type, 'POST'); + $this->enableService('entity:' . $entity_type, 'POST'); // Create two accounts that have the required permissions to create // resources. The second one has administrative permissions. $accounts = $this->createAccountPerEntity($entity_type); @@ -230,7 +230,7 @@ public function testCreateNode() { public function testCreateUser() { $entity_type = 'user'; // Enables the REST service for 'user' entity type. - $this->enableService('entity__' . $entity_type, 'POST'); + $this->enableService('entity:' . $entity_type, 'POST'); // Create two accounts that have the required permissions to create // resources. The second one has administrative permissions. $accounts = $this->createAccountPerEntity($entity_type); @@ -287,7 +287,7 @@ public function createAccountPerEntity($entity_type) { // Get the necessary user permissions for the current $entity_type creation. $permissions = $this->entityPermissions($entity_type, 'create'); // POST method must be allowed for the current entity type. - $permissions[] = 'restful post entity__' . $entity_type; + $permissions[] = 'restful post entity:' . $entity_type; // Create user without administrative permissions. $accounts[] = $this->drupalCreateUser($permissions); // Add administrative permissions for nodes and users. diff --git a/core/modules/rest/src/Tests/CsrfTest.php b/core/modules/rest/src/Tests/CsrfTest.php index e1490d6..a686f448 100644 --- a/core/modules/rest/src/Tests/CsrfTest.php +++ b/core/modules/rest/src/Tests/CsrfTest.php @@ -41,12 +41,12 @@ class CsrfTest extends RESTTestBase { protected function setUp() { parent::setUp(); - $this->enableService('entity__' . $this->testEntityType, 'POST', 'hal_json', array('basic_auth', 'cookie')); + $this->enableService('entity:' . $this->testEntityType, 'POST', 'hal_json', array('basic_auth', 'cookie')); // Create a user account that has the required permissions to create // resources via the REST API. $permissions = $this->entityPermissions($this->testEntityType, 'create'); - $permissions[] = 'restful post entity__' . $this->testEntityType; + $permissions[] = 'restful post entity:' . $this->testEntityType; $this->account = $this->drupalCreateUser($permissions); // Serialize an entity to a string to use in the content body of the POST diff --git a/core/modules/rest/src/Tests/DeleteTest.php b/core/modules/rest/src/Tests/DeleteTest.php index b190878..b95ee9f 100644 --- a/core/modules/rest/src/Tests/DeleteTest.php +++ b/core/modules/rest/src/Tests/DeleteTest.php @@ -33,11 +33,11 @@ public function testDelete() { // controllers are implemented. $entity_types = array('entity_test', 'node'); foreach ($entity_types as $entity_type) { - $this->enableService('entity__' . $entity_type, 'DELETE'); + $this->enableService('entity:' . $entity_type, 'DELETE'); // Create a user account that has the required permissions to delete // resources via the REST API. $permissions = $this->entityPermissions($entity_type, 'delete'); - $permissions[] = 'restful delete entity__' . $entity_type; + $permissions[] = 'restful delete entity:' . $entity_type; $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php index d3d3ec0..8fecfd5 100644 --- a/core/modules/rest/src/Tests/NodeTest.php +++ b/core/modules/rest/src/Tests/NodeTest.php @@ -35,9 +35,9 @@ class NodeTest extends RESTTestBase { * The operation, one of 'view', 'create', 'update' or 'delete'. */ protected function enableNodeConfiguration($method, $operation) { - $this->enableService('entity__node', $method); + $this->enableService('entity:node', $method); $permissions = $this->entityPermissions('node', $operation); - $permissions[] = 'restful ' . strtolower($method) . ' entity__node'; + $permissions[] = 'restful ' . strtolower($method) . ' entity:node'; $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); } @@ -57,7 +57,7 @@ public function testNodes() { // Also check that JSON works and the routing system selects the correct // REST route. - $this->enableService('entity__node', 'GET', 'json'); + $this->enableService('entity:node', 'GET', 'json'); $this->httpRequest($node->urlInfo(), 'GET', NULL, 'application/json'); $this->assertResponse(200); $this->assertHeader('Content-type', 'application/json'); diff --git a/core/modules/rest/src/Tests/PageCacheTest.php b/core/modules/rest/src/Tests/PageCacheTest.php index 2d8f321..6696bf7 100644 --- a/core/modules/rest/src/Tests/PageCacheTest.php +++ b/core/modules/rest/src/Tests/PageCacheTest.php @@ -25,11 +25,11 @@ class PageCacheTest extends RESTTestBase { * Tests that configuration changes also clear the page cache. */ public function testConfigChangePageCache() { - $endpoint_id = 'entity__entity_test'; + $endpoint_id = 'entity:entity_test'; $this->enableService($endpoint_id, 'GET'); // Allow anonymous users to issue GET requests. $permissions = $this->entityPermissions('entity_test', 'view'); - $permissions[] = 'restful get entity__entity_test'; + $permissions[] = 'restful get entity:entity_test'; user_role_grant_permissions('anonymous', $permissions); // Create an entity programmatically. @@ -39,14 +39,14 @@ public function testConfigChangePageCache() { $this->httpRequest($entity->urlInfo(), 'GET', NULL, $this->defaultMimeType); $this->assertResponse(200, 'HTTP response code is correct.'); $this->assertHeader('x-drupal-cache', 'MISS'); - $this->assertCacheTag('config:entity__entity_test'); + $this->assertCacheTag('config:entity:entity_test'); $this->assertCacheTag('entity_test:1'); // Read it again, should be page-cached now. $this->httpRequest($entity->urlInfo(), 'GET', NULL, $this->defaultMimeType); $this->assertResponse(200, 'HTTP response code is correct.'); $this->assertHeader('x-drupal-cache', 'HIT'); - $this->assertCacheTag('config:entity__entity_test'); + $this->assertCacheTag('config:entity:entity_test'); $this->assertCacheTag('entity_test:1'); // Trigger an endpoint save which should clear the page cache, so we should get @@ -55,7 +55,7 @@ public function testConfigChangePageCache() { $this->httpRequest($entity->urlInfo(), 'GET', NULL, $this->defaultMimeType); $this->assertResponse(200, 'HTTP response code is correct.'); $this->assertHeader('x-drupal-cache', 'MISS'); - $this->assertCacheTag('config:entity__entity_test'); + $this->assertCacheTag('config:entity:entity_test'); $this->assertCacheTag('entity_test:1'); } diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php index 220ed57..650c36e 100644 --- a/core/modules/rest/src/Tests/RESTTestBase.php +++ b/core/modules/rest/src/Tests/RESTTestBase.php @@ -244,7 +244,8 @@ protected function entityValues($entity_type) { protected function enableService($resource_type, $method = 'GET', $format = NULL, $auth = NULL) { // Enable REST API for this entity type. /** @var \Drupal\rest\RestEndpointInterface $endpoint */ - $endpoint = $this->endpoint_storage->create(['plugin_id' => $resource_type]); + $endpoint_id = str_replace(':', '__', $resource_type); + $endpoint = $this->endpoint_storage->create(['id' => $endpoint_id]); $settings = array(); if ($resource_type) { diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php index e02a878..e7d29e9 100644 --- a/core/modules/rest/src/Tests/ReadTest.php +++ b/core/modules/rest/src/Tests/ReadTest.php @@ -32,11 +32,11 @@ public function testRead() { // Define the entity types we want to test. $entity_types = array('entity_test', 'node'); foreach ($entity_types as $entity_type) { - $this->enableService('entity__' . $entity_type, 'GET'); + $this->enableService('entity:' . $entity_type, 'GET'); // Create a user account that has the required permissions to read // resources via the REST API. $permissions = $this->entityPermissions($entity_type, 'view'); - $permissions[] = 'restful get entity__' . $entity_type; + $permissions[] = 'restful get entity:' . $entity_type; $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); @@ -100,11 +100,11 @@ public function testRead() { */ public function testResourceStructure() { // Enable a service with a format restriction but no authentication. - $this->enableService('entity__node', 'GET', 'json'); + $this->enableService('entity:node', 'GET', 'json'); // Create a user account that has the required permissions to read // resources via the REST API. $permissions = $this->entityPermissions('node', 'view'); - $permissions[] = 'restful get entity__node'; + $permissions[] = 'restful get entity:node'; $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); diff --git a/core/modules/rest/src/Tests/ResourceTest.php b/core/modules/rest/src/Tests/ResourceTest.php index edb3b18..99a7edb 100644 --- a/core/modules/rest/src/Tests/ResourceTest.php +++ b/core/modules/rest/src/Tests/ResourceTest.php @@ -36,7 +36,7 @@ protected function setUp() { */ public function testFormats() { /** @var \Drupal\rest\RestEndpointInterface $endpoint */ - $endpoint = $this->endpoint_storage->create(['plugin_id' => 'entity__entity_test']); + $endpoint = $this->endpoint_storage->create(['id' => 'entity.entity_test']); $endpoint->setSettings([ 'GET' => [ 'supported_auth' => [ @@ -63,7 +63,7 @@ public function testFormats() { */ public function testAuthentication() { /** @var \Drupal\rest\RestEndpointInterface $endpoint */ - $endpoint = $this->endpoint_storage->create(['plugin_id' => 'entity__entity_test']); + $endpoint = $this->endpoint_storage->create(['id' => 'entity.entity_test']); $endpoint->setSettings([ 'GET' => [ 'supported_formats' => [ diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php index 23cf9fe..481e33b 100644 --- a/core/modules/rest/src/Tests/UpdateTest.php +++ b/core/modules/rest/src/Tests/UpdateTest.php @@ -32,11 +32,11 @@ public function testPatchUpdate() { // @todo Test all other entity types here as well. $entity_type = 'entity_test'; - $this->enableService('entity__' . $entity_type, 'PATCH'); + $this->enableService('entity:' . $entity_type, 'PATCH'); // Create a user account that has the required permissions to create // resources via the REST API. $permissions = $this->entityPermissions($entity_type, 'update'); - $permissions[] = 'restful patch entity__' . $entity_type; + $permissions[] = 'restful patch entity:' . $entity_type; $account = $this->drupalCreateUser($permissions); $this->drupalLogin($account); @@ -170,9 +170,9 @@ public function testUpdateUser() { $serializer = $this->container->get('serializer'); $entity_type = 'user'; // Enables the REST service for 'user' entity type. - $this->enableService('entity__' . $entity_type, 'PATCH'); + $this->enableService('entity:' . $entity_type, 'PATCH'); $permissions = $this->entityPermissions($entity_type, 'update'); - $permissions[] = 'restful patch entity__' . $entity_type; + $permissions[] = 'restful patch entity:' . $entity_type; $account = $this->drupalCreateUser($permissions); $account->set('mail', 'old-email@example.com'); $this->drupalLogin($account);