.../config/optional/rest.resource.entity.node.yml | 31 ++-- core/modules/rest/config/schema/rest.schema.yml | 24 ++- core/modules/rest/rest.install | 2 + core/modules/rest/rest.post_update.php | 41 ++++- .../modules/rest/src/Entity/ConfigDependencies.php | 97 ++++++++--- .../modules/rest/src/Entity/RestResourceConfig.php | 11 +- .../rest/src/RestResourceConfigInterface.php | 5 + .../Tests/Update/ResourceGranularityUpdateTest.php | 71 ++++++++ .../Update/RestConfigurationEntitiesUpdateTest.php | 14 +- ....rest-rest_post_update_resource_granularity.php | Bin 0 -> 5322 bytes .../rest.resource.entity.comment_2721595.yml | 30 ++++ .../update/rest.resource.entity.node_2721595.yml | 29 ++++ .../update/rest.resource.entity.user_2721595.yml | 30 ++++ .../src/Kernel/Entity/ConfigDependenciesTest.php | 187 ++++++++++++++++----- .../src/Tests/System/ResponseGeneratorTest.php | 13 +- 15 files changed, 477 insertions(+), 108 deletions(-) diff --git a/core/modules/rest/config/optional/rest.resource.entity.node.yml b/core/modules/rest/config/optional/rest.resource.entity.node.yml index 0cf4d78..39f7713 100644 --- a/core/modules/rest/config/optional/rest.resource.entity.node.yml +++ b/core/modules/rest/config/optional/rest.resource.entity.node.yml @@ -1,27 +1,16 @@ id: entity.node plugin_id: 'entity:node' -granularity: method +granularity: resource 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 + methods: + - GET + - POST + - PATCH + - DELETE + formats: + - hal_json + authentication: + - basic_auth dependencies: module: - node diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml index 41bc2bf..04f88a6 100644 --- a/core/modules/rest/config/schema/rest.schema.yml +++ b/core/modules/rest/config/schema/rest.schema.yml @@ -8,7 +8,6 @@ rest.settings: label: 'Domain of the relation' # Method-level granularity of REST resource configuration. -# @todo Add resource-level granularity in https://www.drupal.org/node/2721595. rest_resource.method: type: mapping mapping: @@ -25,6 +24,29 @@ rest_resource.method: type: rest_request label: 'DELETE method settings' +# Resource-level granularity of REST resource configuration. +rest_resource.resource: + type: mapping + mapping: + methods: + type: sequence + label: 'Supported methods' + sequence: + type: string + label: 'HTTP method' + formats: + type: sequence + label: 'Supported formats' + sequence: + type: string + label: 'Format' + authentication: + type: sequence + label: 'Supported authentication providers' + sequence: + type: string + label: 'Authentication provider' + rest_request: type: mapping mapping: diff --git a/core/modules/rest/rest.install b/core/modules/rest/rest.install index 918b4f5..4cfaa11 100644 --- a/core/modules/rest/rest.install +++ b/core/modules/rest/rest.install @@ -30,6 +30,8 @@ function rest_requirements($phase) { /** * Install the REST config entity type and fix old settings-based config. + * + * @see rest_post_update_create_rest_resource_config_entities() */ function rest_update_8201() { \Drupal::entityDefinitionUpdateManager()->installEntityType(\Drupal::entityTypeManager()->getDefinition('rest_resource_config')); diff --git a/core/modules/rest/rest.post_update.php b/core/modules/rest/rest.post_update.php index 7068662..445c617 100644 --- a/core/modules/rest/rest.post_update.php +++ b/core/modules/rest/rest.post_update.php @@ -16,11 +16,8 @@ /** * Create REST resource configuration entities. * - * @todo https://www.drupal.org/node/2721595 Automatically upgrade those REST - * resource config entities that have the same formats/auth mechanisms for all - * methods to "granular: resource". - * * @see rest_update_8201() + * @see https://www.drupal.org/node/2308745 */ function rest_post_update_create_rest_resource_config_entities() { $resources = \Drupal::state()->get('rest_update_8201_resources', []); @@ -34,6 +31,42 @@ function rest_post_update_create_rest_resource_config_entities() { } } +/** + * Simplify method-granularity REST resource config to resource-granularity. + * + * @see https://www.drupal.org/node/2721595 + */ +function rest_post_update_resource_granularity() { + /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_config_entities */ + $resource_config_entities = RestResourceConfig::loadMultiple(); + + foreach ($resource_config_entities as $resource_config_entity) { + if ($resource_config_entity->get('granularity') === RestResourceConfigInterface::METHOD_GRANULARITY) { + $configuration = $resource_config_entity->get('configuration'); + + $format_and_auth_configuration = []; + foreach (array_keys($configuration) as $method) { + $format_and_auth_configuration['format'][$method] = implode(',', $configuration[$method]['supported_formats']); + $format_and_auth_configuration['auth'][$method] = implode(',', $configuration[$method]['supported_auth']); + } + + // If each method has the same formats and the same authentication + // providers configured, convert it to 'granularity: resource', which has + // a simpler/less verbose configuration. + if (count(array_unique($format_and_auth_configuration['format'])) === 1 && count(array_unique($format_and_auth_configuration['auth'])) === 1) { + $resource_config_entity->set('configuration', [ + 'methods' => array_keys($configuration), + 'formats' => $configuration['GET']['supported_formats'], + 'authentication' => $configuration['GET']['supported_auth'] + ]); + $resource_config_entity->set('granularity', RestResourceConfigInterface::RESOURCE_GRANULARITY); + $resource_config_entity->save(); + } + } + } +} + + /** * @} End of "addtogroup updates-8.1.x-to-8.2.x". */ diff --git a/core/modules/rest/src/Entity/ConfigDependencies.php b/core/modules/rest/src/Entity/ConfigDependencies.php index 72fc8cc..5362d91 100644 --- a/core/modules/rest/src/Entity/ConfigDependencies.php +++ b/core/modules/rest/src/Entity/ConfigDependencies.php @@ -61,31 +61,21 @@ public static function create(ContainerInterface $container) { */ public function calculateDependencies(RestResourceConfigInterface $rest_config) { $granularity = $rest_config->get('granularity'); + + // Dependency calculation is the same for either granularity, the most + // notable difference is that for the 'resource' granularity, the same + // authentication providers and formats are supported for every method. if ($granularity === RestResourceConfigInterface::METHOD_GRANULARITY) { - return $this->calculateDependenciesForMethodGranularity($rest_config); + $methods = array_keys($rest_config->get('configuration')); } else { - throw new \InvalidArgumentException("A different granularity then 'method' is not supported yet."); - // @todo Add resource-level granularity support in https://www.drupal.org/node/2721595. + $methods = ['GET']; } - } - /** - * Calculates dependencies of a specific rest resource configuration. - * - * @param \Drupal\rest\RestResourceConfigInterface $rest_config - * The rest configuration. - * - * @return string[][] - * Dependencies keyed by dependency type. - * - * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies() - */ - protected function calculateDependenciesForMethodGranularity(RestResourceConfigInterface $rest_config) { // The dependency lists for authentication providers and formats // generated on container build. $dependencies = []; - foreach (array_keys($rest_config->get('configuration')) as $request_method) { + foreach ($methods as $request_method) { // Add dependencies based on the supported authentication providers. foreach ($rest_config->getAuthenticationProviders($request_method) as $auth) { if (isset($this->authProviders[$auth])) { @@ -102,6 +92,10 @@ protected function calculateDependenciesForMethodGranularity(RestResourceConfigI } } + if (isset($dependencies['module'])) { + sort($dependencies['module']); + } + return $dependencies; } @@ -125,8 +119,7 @@ public function onDependencyRemoval(RestResourceConfigInterface $rest_config, ar return $this->onDependencyRemovalForMethodGranularity($rest_config, $dependencies); } else { - throw new \InvalidArgumentException("A different granularity then 'method' is not supported yet."); - // @todo Add resource-level granularity support in https://www.drupal.org/node/2721595. + return $this->onDependencyRemovalForResourceGranularity($rest_config, $dependencies); } } @@ -183,7 +176,71 @@ protected function onDependencyRemovalForMethodGranularity(RestResourceConfigInt } } } - if (!empty($configuration_before != $configuration)) { + if ($configuration_before != $configuration && !empty($configuration)) { + $rest_config->set('configuration', $configuration); + // Only mark the dependencies problems as fixed if there is any + // configuration left. + $changed = TRUE; + } + } + // If the dependency problems are not marked as fixed at this point they + // should be related to the resource plugin and the config entity should + // be deleted. + return $changed; + } + + /** + * Informs the entity that entities it depends on will be deleted. + * + * @param \Drupal\rest\RestResourceConfigInterface $rest_config + * The rest configuration. + * @param array $dependencies + * An array of dependencies that will be deleted keyed by dependency type. + * Dependency types are, for example, entity, module and theme. + * + * @return bool + * TRUE if the entity has been changed as a result, FALSE if not. + */ + public function onDependencyRemovalForResourceGranularity(RestResourceConfigInterface $rest_config, array $dependencies) { + $changed = FALSE; + // Only module-related dependencies can be fixed. All other types of + // dependencies cannot, because they were not generated based on supported + // authentication providers or formats. + if (isset($dependencies['module'])) { + // Try to fix dependencies. + $removed_auth = array_keys(array_intersect($this->authProviders, $dependencies['module'])); + $removed_formats = array_keys(array_intersect($this->formatProviders, $dependencies['module'])); + $configuration_before = $configuration = $rest_config->get('configuration'); + if (!empty($removed_auth) || !empty($removed_formats)) { + // Try to fix dependency problems by removing affected + // authentication providers and formats. + foreach ($removed_formats as $format) { + if (in_array($format, $rest_config->getFormats('GET'))) { + $configuration['formats'] = array_diff($configuration['formats'], $removed_formats); + } + } + foreach ($removed_auth as $auth) { + if (in_array($auth, $rest_config->getAuthenticationProviders('GET'))) { + $configuration['authentication'] = array_diff($configuration['authentication'], $removed_auth); + } + } + if (empty($configuration['authentication'])) { + // Remove the key if there are no more authentication providers + // supported. + unset($configuration['authentication']); + } + if (empty($configuration['formats'])) { + // Remove the key if there are no more formats supported. + unset($configuration['formats']); + } + if (empty($configuration['authentication']) || empty($configuration['formats'])) { + // If there no longer are any supported authentication providers or + // formats, this REST resource can no longer function, and so we + // cannot fix this config entity to keep it working. + $configuration = []; + } + } + if ($configuration_before != $configuration && !empty($configuration)) { $rest_config->set('configuration', $configuration); // Only mark the dependencies problems as fixed if there is any // configuration left. diff --git a/core/modules/rest/src/Entity/RestResourceConfig.php b/core/modules/rest/src/Entity/RestResourceConfig.php index f2b8b0e..bc7efd6 100644 --- a/core/modules/rest/src/Entity/RestResourceConfig.php +++ b/core/modules/rest/src/Entity/RestResourceConfig.php @@ -45,7 +45,7 @@ class RestResourceConfig extends ConfigEntityBase implements RestResourceConfigI /** * The REST resource configuration granularity. * - * @todo Currently only 'method', but https://www.drupal.org/node/2721595 will add 'resource' + * Currently either 'method' or 'resource'. * * @var string */ @@ -116,8 +116,7 @@ public function getMethods() { return $this->getMethodsForMethodGranularity(); } else { - throw new \InvalidArgumentException("A different granularity then 'method' is not supported yet."); - // @todo Add resource-level granularity support in https://www.drupal.org/node/2721595. + return $this->configuration['methods']; } } @@ -140,8 +139,7 @@ public function getAuthenticationProviders($method) { return $this->getAuthenticationProvidersForMethodGranularity($method); } else { - throw new \InvalidArgumentException("A different granularity then 'method' is not supported yet."); - // @todo Add resource-level granularity support in https://www.drupal.org/node/2721595. + return $this->configuration['authentication']; } } @@ -170,8 +168,7 @@ public function getFormats($method) { return $this->getFormatsForMethodGranularity($method); } else { - throw new \InvalidArgumentException("A different granularity then 'method' is not supported yet."); - // @todo Add resource-level granularity support in https://www.drupal.org/node/2721595. + return $this->configuration['formats']; } } diff --git a/core/modules/rest/src/RestResourceConfigInterface.php b/core/modules/rest/src/RestResourceConfigInterface.php index 7f34bc4..601bb0e 100644 --- a/core/modules/rest/src/RestResourceConfigInterface.php +++ b/core/modules/rest/src/RestResourceConfigInterface.php @@ -15,6 +15,11 @@ */ const METHOD_GRANULARITY = 'method'; + /** + * Granularity value for per-resource configuration. + */ + const RESOURCE_GRANULARITY = 'resource'; + /** * Retrieves the REST resource plugin. * diff --git a/core/modules/rest/src/Tests/Update/ResourceGranularityUpdateTest.php b/core/modules/rest/src/Tests/Update/ResourceGranularityUpdateTest.php new file mode 100644 index 0000000..c8cf89a --- /dev/null +++ b/core/modules/rest/src/Tests/Update/ResourceGranularityUpdateTest.php @@ -0,0 +1,71 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../../rest/tests/fixtures/update/drupal-8.rest-rest_post_update_resource_granularity.php', + ]; + } + + /** + * Tests rest_post_update_simplify_resource_granularity(). + */ + public function testMethodGranularityConvertedToResourceGranularity() { + /** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */ + $resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config'); + + // Make sure we have the expected values before the update. + $resource_config_entities = $resource_config_storage->loadMultiple(); + $this->assertIdentical(['entity.comment', 'entity.node', 'entity.user'], array_keys($resource_config_entities)); + $this->assertIdentical('method', $resource_config_entities['entity.node']->get('granularity')); + $this->assertIdentical('method', $resource_config_entities['entity.comment']->get('granularity')); + $this->assertIdentical('method', $resource_config_entities['entity.user']->get('granularity')); + + // Read the existing 'entity:comment' and 'entity:user' resource + // configuration so we can verify it after the update. + $comment_resource_configuration = $resource_config_entities['entity.comment']->get('configuration'); + $user_resource_configuration = $resource_config_entities['entity.user']->get('configuration'); + + $this->runUpdates(); + + // Make sure we have the expected values after the update. + $resource_config_entities = $resource_config_storage->loadMultiple(); + $this->assertIdentical(['entity.comment', 'entity.node', 'entity.user'], array_keys($resource_config_entities)); + // 'entity:node' should be updated. + $this->assertIdentical('resource', $resource_config_entities['entity.node']->get('granularity')); + $this->assertidentical($resource_config_entities['entity.node']->get('configuration'), [ + 'methods' => ['GET', 'POST', 'PATCH', 'DELETE'], + 'formats' => ['hal_json'], + 'authentication' => ['basic_auth'], + ]); + // 'entity:comment' should be unchanged. + $this->assertIdentical('method', $resource_config_entities['entity.comment']->get('granularity')); + $this->assertIdentical($comment_resource_configuration, $resource_config_entities['entity.comment']->get('configuration')); + // 'entity:user' should be unchanged. + $this->assertIdentical('method', $resource_config_entities['entity.user']->get('granularity')); + $this->assertIdentical($user_resource_configuration, $resource_config_entities['entity.user']->get('configuration')); + } + +} diff --git a/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php b/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php index 9100f16..097da35 100644 --- a/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php +++ b/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php @@ -9,6 +9,8 @@ * Tests that rest.settings is converted to rest_resource_config entities. * * @see https://www.drupal.org/node/2308745 + * @see rest_update_8201() + * @see rest_post_update_create_rest_resource_config_entities() * * @group rest */ @@ -43,10 +45,6 @@ public function testResourcesConvertedToConfigEntities() { $resource_config_entities = $resource_config_storage->loadMultiple(); $this->assertIdentical([], array_keys($resource_config_entities)); - // Read the existing 'entity:node' resource configuration so we can verify - // it after the update. - $node_configuration = $rest_settings->getRawData()['resources']['entity:node']; - $this->runUpdates(); // Make sure we have the expected values after the update. @@ -55,8 +53,12 @@ public function testResourcesConvertedToConfigEntities() { $resource_config_entities = $resource_config_storage->loadMultiple(); $this->assertIdentical(['entity.node'], array_keys($resource_config_entities)); $node_resource_config_entity = $resource_config_entities['entity.node']; - $this->assertIdentical(RestResourceConfigInterface::METHOD_GRANULARITY, $node_resource_config_entity->get('granularity')); - $this->assertIdentical($node_configuration, $node_resource_config_entity->get('configuration')); + $this->assertIdentical(RestResourceConfigInterface::RESOURCE_GRANULARITY, $node_resource_config_entity->get('granularity')); + $this->assertIdentical([ + 'methods' => ['GET'], + 'formats' => ['json'], + 'authentication' => ['basic_auth'], + ], $node_resource_config_entity->get('configuration')); $this->assertIdentical(['module' => ['basic_auth', 'node', 'serialization']], $node_resource_config_entity->getDependencies()); } diff --git a/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_post_update_resource_granularity.php b/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_post_update_resource_granularity.php new file mode 100644 index 0000000000000000000000000000000000000000..d657afcf9c13b177c952c1f144825d7b6a007ee3 GIT binary patch literal 5322 zcmcIoZExH*67FaJ3Sv=U?I2!f*LGgCSKl3+6n8+I7IFIF5C;S;(QCqzD3FwEC&+*A zGaO1Qd3TE*ntX_(<;%Q0^UQF_fBdt#Y4-Mxo;=wTPsG2jO{wYQ+}1&v+KWO3b)~!( zswhk_7DK^`!nKVm4`0r%l+!aGR9z@n%phvE`XsKc6G8i6>XisLQC{1uoGP?vRJalL zS~yJ0#^O)gK!uhVu&v9r#JajIl{4XC|6m4Nj`sFi=zkqmUY^^kv9+$l<%M=emFANo zt1kbis&YAA==A05WPSg!+}ry;w{@*^+9iGv-HI$*=>V_O%>DsZ9vz7b9moaY^BY|$ zai^WfzS&U7;Tdq!F6>Wl^@F@qWvizLdqNzZT^n5%e*Xi02r znz7{RQ(jc7O7k=!)Qz&xxXWZO7tiL)IP>u95^I@%w53@XJdqh(uS)#p^>)J z!E$(z$ai?gNwFQFM5hQ2QKAz#wvzbtt)Lqx(cxL8-AZrLe-M&!$dJ9r?pdL)P0clB zh7<}4VJ&utGnS0LOh7oeGh@WG9Pk(w7lbhw~`o@=Y`+{o)_*A#ai$ zzsf%QZ2ml(eD~x#Rwx?>tM4aEpS{c`oi+4xej1m-jbL(_D-?{$@+x~qQzvn%wDz`b zB&vj;ELAqoKASj=*|s&sWclT>GJW4z7ijTGw-_$aQj^Ug_*FJ(%64UH$zuyrHqR!5 z8cIu#Y4|vsunVn=)QjpCSPb7{01JlWO|SD=_L(eVznrcM?bd#g`Z@GtcSA)HwX6=0 zrrGTjqR#E-glY0(@Db6xGgLbW&1cz!{sZ(^ZC#XrvLD0BQq`;67IE~^Pl^_ZrP?yc z;j&%hJ@_6Nuj9Z@6RNNg#%v=W%@NuJfwOl8_|Oo<&}#aGZN!{Xc|_c2$1i&R(Ta|E z@9>R(@%ZByM9EA$>kQ(n%5xU-cCGUUhi(bzi5?3A6&qBZYu0rF4~AS%B1l!hGo!Am{=c3z0d|^_kq5>^Jjmr2gs0-K>$B8X2_5k!J zIvxR{1x%>4YuF7$Pyl!06Qw*2F~+5-ZzB?kwD1IiN_C}6kg_bVRDK%;U^x!@pJ?Jo zXRBXh`p`^D1*V%EDT$zivXGdj?e%0CN7>*s8r7uUKyKmz{(RxjI>*`r-GG5f{OsL3^JzQV-RAlaQa4 zL$N{)BxZ3)ecTYSLPorMbMd~99!`HM#}Q$6QMpxH0mWfZunQj8Cbp48TM8CYW*99G zqp1HQS*Z%762#F&!E*`@t!KH+q*&XwjsRYaS>|C z*N2g`avJqD8Cw4j{BVTCqy_o`y-V~2SaOmJ$`FxUB$yNVVpl3#BO^J1KaEuVwTkoop?3^7nKT*I!gc@8gOcR) ziO5mFF!+Zn$%s*$6W!H>Xu+i7q7AY!XFFoY!{zPrOV{U)yY0s|jGBwopAQS3a#A-$w?m5tz^fj3`G9^KDSMsp-4 z?rI#O#M!P?7P_Gpq0Ws)q`5yiMFKP324;5~)&xr@ne;l4)wG*BnG^7FbGKJ_u%N{` zRmpT%p((RCX~IL-?3}`ftJ3DTBNqzesj|s9tw2?*IlqM%AcnuY0(tWyfXS(IawoqM}68*2NbWq*S=d=@S-t-sOPE*3z0y!wX z`J{(1y`XK#Bvawc9+}D`K0LtT$Dw(dc5OQNXS-hQbdHV2t-n$M2m6M;IW@s2IJ!3# z_Lr9}yQsxH1_w5G1$A$0TRlhv#@)-1I$|QtDL{+`WyrYP>Ga01S!iMgN6v033mb{F zqYMm`4R7W 'hal', 'json' => 'serialization'], ['basic_auth' => 'basic_auth']); - $rest_config = RestResourceConfig::create([ - 'plugin_id' => 'entity:entity_test', - 'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY, - 'configuration' => [ - 'GET' => [ - 'supported_auth' => ['cookie'], - 'supported_formats' => ['json'], - ], - 'POST' => [ - 'supported_auth' => ['basic_auth'], - 'supported_formats' => ['hal_json'], - ], - ], - ]); + $rest_config = RestResourceConfig::create($configuration); $result = $config_dependencies->calculateDependencies($rest_config); $this->assertEquals(['module' => [ - 'serialization', 'basic_auth', 'hal', + 'basic_auth', 'hal', 'serialization', ]], $result); } /** * @covers ::onDependencyRemoval - * @covers ::calculateDependenciesForMethodGranularity + * @covers ::onDependencyRemovalForMethodGranularity + * @covers ::onDependencyRemovalForResourceGranularity + * + * @dataProvider providerBasicDependencies */ - public function testOnDependencyRemovalRemoveUnrelatedDependency() { + public function testOnDependencyRemovalRemoveUnrelatedDependency(array $configuration) { $config_dependencies = new ConfigDependencies(['hal_json' => 'hal', 'json' => 'serialization'], ['basic_auth' => 'basic_auth']); - $rest_config = RestResourceConfig::create([ - 'plugin_id' => 'entity:entity_test', - 'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY, - 'configuration' => [ - 'GET' => [ - 'supported_auth' => ['cookie'], - 'supported_formats' => ['json'], - ], - 'POST' => [ - 'supported_auth' => ['basic_auth'], - 'supported_formats' => ['hal_json'], - ], - ], - ]); + $rest_config = RestResourceConfig::create($configuration); $this->assertFalse($config_dependencies->onDependencyRemoval($rest_config, ['module' => ['node']])); - $this->assertEquals([ - 'GET' => [ - 'supported_auth' => ['cookie'], - 'supported_formats' => ['json'], + $this->assertEquals($configuration['configuration'], $rest_config->get('configuration')); + } + + /** + * @return array + * An array with numerical keys: + * 0. The original REST resource configuration. + */ + public function providerBasicDependencies() { + return [ + 'method' => [ + [ + 'plugin_id' => 'entity:entity_test', + 'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY, + 'configuration' => [ + 'GET' => [ + 'supported_auth' => ['cookie'], + 'supported_formats' => ['json'], + ], + 'POST' => [ + 'supported_auth' => ['basic_auth'], + 'supported_formats' => ['hal_json'], + ], + ], + ], ], - 'POST' => [ - 'supported_auth' => ['basic_auth'], - 'supported_formats' => ['hal_json'], + 'resource' => [ + [ + 'plugin_id' => 'entity:entity_test', + 'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY, + 'configuration' => [ + 'methods' => ['GET', 'POST'], + 'formats' => ['json', 'hal_json'], + 'authentication' => ['cookie', 'basic_auth'], + ], + ], ], - ], $rest_config->get('configuration')); + ]; } /** * @covers ::onDependencyRemoval - * @covers ::calculateDependenciesForMethodGranularity + * @covers ::onDependencyRemovalForMethodGranularity */ - public function testOnDependencyRemovalRemoveFormat() { + public function testOnDependencyRemovalRemoveFormatForMethodGranularity() { $config_dependencies = new ConfigDependencies(['hal_json' => 'hal', 'json' => 'serialization'], ['basic_auth' => 'basic_auth']); $rest_config = RestResourceConfig::create([ @@ -120,7 +126,7 @@ public function testOnDependencyRemovalRemoveFormat() { /** * @covers ::onDependencyRemoval - * @covers ::calculateDependenciesForMethodGranularity + * @covers ::onDependencyRemovalForMethodGranularity */ public function testOnDependencyRemovalRemoveAuth() { $config_dependencies = new ConfigDependencies(['hal_json' => 'hal', 'json' => 'serialization'], ['basic_auth' => 'basic_auth']); @@ -156,7 +162,7 @@ public function testOnDependencyRemovalRemoveAuth() { /** * @covers ::onDependencyRemoval - * @covers ::calculateDependenciesForMethodGranularity + * @covers ::onDependencyRemovalForMethodGranularity */ public function testOnDependencyRemovalRemoveAuthAndFormats() { $config_dependencies = new ConfigDependencies(['hal_json' => 'hal', 'json' => 'serialization'], ['basic_auth' => 'basic_auth']); @@ -189,4 +195,95 @@ public function testOnDependencyRemovalRemoveAuthAndFormats() { ], $rest_config->get('configuration')); } + /** + * @covers ::onDependencyRemoval + * @covers ::onDependencyRemovalForResourceGranularity + * + * @dataProvider providerOnDependencyRemovalForResourceGranularity + */ + public function testOnDependencyRemovalForResourceGranularity(array $configuration, $module, $expected_configuration) { + assert('is_string($module)'); + assert('$expected_configuration === FALSE || is_array($expected_configuration)'); + $config_dependencies = new ConfigDependencies(['hal_json' => 'hal', 'json' => 'serialization'], ['basic_auth' => 'basic_auth']); + + $rest_config = RestResourceConfig::create($configuration); + + $this->assertSame(!empty($expected_configuration), $config_dependencies->onDependencyRemoval($rest_config, ['module' => [$module]])); + if (!empty($expected_configuration)) { + $this->assertEquals($expected_configuration, $rest_config->get('configuration')); + } + } + + /** + * @return array + * An array with numerical keys: + * 0. The original REST resource configuration. + * 1. The module to uninstall (the dependency that is about to be removed). + * 2. The expected configuration after uninstalling this module. + */ + public function providerOnDependencyRemovalForResourceGranularity() { + return [ + 'resource with multiple formats' => [ + [ + 'plugin_id' => 'entity:entity_test', + 'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY, + 'configuration' => [ + 'methods' => ['GET', 'POST'], + 'formats' => ['json', 'hal_json'], + 'authentication' => ['cookie', 'basic_auth'], + ], + ], + 'hal', + [ + 'methods' => ['GET', 'POST'], + 'formats' => ['json'], + 'authentication' => ['cookie', 'basic_auth'], + ] + ], + 'resource with only HAL+JSON format' => [ + [ + 'plugin_id' => 'entity:entity_test', + 'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY, + 'configuration' => [ + 'methods' => ['GET', 'POST'], + 'formats' => ['hal_json'], + 'authentication' => ['cookie', 'basic_auth'], + ], + ], + 'hal', + FALSE + ], + 'resource with multiple authentication providers' => [ + [ + 'plugin_id' => 'entity:entity_test', + 'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY, + 'configuration' => [ + 'methods' => ['GET', 'POST'], + 'formats' => ['json', 'hal_json'], + 'authentication' => ['cookie', 'basic_auth'], + ], + ], + 'basic_auth', + [ + 'methods' => ['GET', 'POST'], + 'formats' => ['json', 'hal_json'], + 'authentication' => ['cookie'], + ] + ], + 'resource with only basic_auth authentication' => [ + [ + 'plugin_id' => 'entity:entity_test', + 'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY, + 'configuration' => [ + 'methods' => ['GET', 'POST'], + 'formats' => ['json', 'hal_json'], + 'authentication' => ['basic_auth'], + ], + ], + 'basic_auth', + FALSE, + ], + ]; + } + } diff --git a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php index d0d22a2..d2f8f5f 100644 --- a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php +++ b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php @@ -53,13 +53,18 @@ function testGeneratorHeaderAdded() { $this->assertEqual('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type')); $this->assertEqual($expectedGeneratorHeader, $this->drupalGetHeader('X-Generator')); - // Enable rest API for nodes - $this->enableService('entity:node', 'GET', 'json'); + // Enable cookie-based authentication for the entity:node REST resource. + /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */ + $resource_config = $this->resourceConfigStorage->load('entity.node'); + $configuration = $resource_config->get('configuration'); + $configuration['authentication'][] = 'cookie'; + $resource_config->set('configuration', $configuration)->save(); + $this->rebuildCache(); // Tests to see if this also works for a non-html request - $this->httpRequest($node->urlInfo()->setOption('query', ['_format' => 'json']), 'GET'); + $this->httpRequest($node->urlInfo()->setOption('query', ['_format' => 'hal_json']), 'GET'); $this->assertResponse(200); - $this->assertEqual('application/json', $this->drupalGetHeader('Content-Type')); + $this->assertEqual('application/hal+json', $this->drupalGetHeader('Content-Type')); $this->assertEqual($expectedGeneratorHeader, $this->drupalGetHeader('X-Generator')); }