Problem/Motivation

Quoting @effulgentsia from #2721595-69: Simplify REST configuration:

Sorry. A few more nits.

  1. +++ b/core/modules/rest/src/Entity/ConfigDependencies.php
    @@ -102,6 +96,10 @@ protected function calculateDependenciesForMethodGranularity(RestResourceConfigI
    +    if (isset($dependencies['module'])) {
    +      sort($dependencies['module']);
    +    }
    

    Either we assume that the result of this function always passes through DependencyTrait::addDependency() (which it does within RestResourceConfig::calculateDependencies()) prior to being saved to storage or otherwise needing to be sorted, or we do not assume this. If we assume it, then we shouldn't need this hunk, but we should add docs to the function to clarify this assumption. If it's only to make a unit test pass, then perhaps the unit test should be the one to sort. If we do not assume this, and therefore, require this function to sort, then it should mimic DependencyTrait::addDependency() and also ensure uniqueness.

  2. +++ b/core/modules/rest/src/Entity/ConfigDependencies.php
    @@ -183,7 +182,71 @@ protected function onDependencyRemovalForMethodGranularity(RestResourceConfigInt
    +          if (in_array($format, $rest_config->getFormats('GET'))) {
    +            $configuration['formats'] = array_diff($configuration['formats'], $removed_formats);
    +          }
    

    Per #64.2, we shouldn't assume GET is defined. Technically, this code doesn't, as the implementation of getFormats() ignores the passed-in method, but that's not obvious from reading this code in isolation. Any reason not to simply use $configuration['formats'] instead of $rest_config->getFormats('GET')?

  3. +++ b/core/modules/rest/src/Entity/ConfigDependencies.php
    @@ -183,7 +182,71 @@ protected function onDependencyRemovalForMethodGranularity(RestResourceConfigInt
    +          if (in_array($auth, $rest_config->getAuthenticationProviders('GET'))) {
    +            $configuration['authentication'] = array_diff($configuration['authentication'], $removed_auth);
    +          }
    

    Same as above, but for getAuthenticationProviders().

And then him again in #2721595-71: Simplify REST configuration:

As I looked more at #69, I couldn't find a reason for those points to block commit, so pushed to 8.2.x. A follow-up for #69 would be appreciated though.

This is that follow-up.

Proposed resolution

Fix the nits.

Remaining tasks

Fix the nits.

User interface changes

None.

API changes

None.

Data model changes

None.

Comments

Wim Leers created an issue. See original summary.

dawehner’s picture

Issue tags: +php-novice

Adding a tag.

wim leers’s picture

Status: Active » Needs review
StatusFileSize
new3.61 KB

#2: Sorry, I started addressing this already. While minor and simple stuff, you kind of need to know how all these things work to address it the appropriate way.

  1. Wow, I can't believe you even noticed such a detail. @dawehner was the one who wrote that code, but AFAICT it's indeed there only to make the unit test pass. Done.
  2. The intent was to use the interface were possible. Addressed by picking the first available method, and requesting formats + auth providers for that.
  3. See 2.
dawehner’s picture

Status: Needs review » Needs work

Well, IMHO we should mimic the behaviour and ensure uniqueness actually. As someone, who builds sites, having deterministic sorting on CMI is a huge deal. It helps you a lot with actual adapting configuration quickly.

wim leers’s picture

Status: Needs work » Needs review

Well, IMHO we should mimic the behaviour and ensure uniqueness actually. As someone, who builds sites, having deterministic sorting on CMI is a huge deal. It helps you a lot with actual adapting configuration quickly.

But we already have that. \Drupal\rest\Entity\ConfigDependencies::calculateDependencies() was just duplicating it. So sorting was happening twice.

Because:

  1. \Drupal\rest\Entity\RestResourceConfig::calculateDependencies() does:
      public function calculateDependencies() {
        parent::calculateDependencies();
    
        foreach ($this->getRestResourceDependencies()->calculateDependencies($this) as $type => $dependencies) {
          foreach ($dependencies as $dependency) {
            $this->addDependency($type, $dependency);
          }
        }
        return $this;
      }
    

    (Note the addDependency() call!!!!!)

  2. The called \Drupal\Core\Config\Entity\ConfigEntityBase::addDependency() does:
      protected function addDependency($type, $name) {
        // A config entity is always dependent on its provider. There is no need to
        // explicitly declare the dependency. An explicit dependency on Core, which
        // provides some plugins, is also not needed.
        if ($type == 'module' && ($name == $this->getEntityType()->getProvider() || $name == 'core')) {
          return $this;
        }
    
        return $this->addDependencyTrait($type, $name);
      }
    
    

    And note: that addDependencyTrait() is an aliased trait method:

      use PluginDependencyTrait {
        addDependency as addDependencyTrait;
      }
    
  3. The called \Drupal\Core\Entity\DependencyTrait::addDependency() finally does:
      protected function addDependency($type, $name) {
        if (empty($this->dependencies[$type])) {
          $this->dependencies[$type] = array($name);
          if (count($this->dependencies) > 1) {
            // Ensure a consistent order of type keys.
            ksort($this->dependencies);
          }
        }
        elseif (!in_array($name, $this->dependencies[$type])) {
          $this->dependencies[$type][] = $name;
          // Ensure a consistent order of dependency names.
          sort($this->dependencies[$type], SORT_FLAG_CASE);
        }
        return $this;
      }
    

In other words: @effulgentsia merely pointed out that we're doing duplicate (yet incomplete, because not guaranteeing uniqueness) work. And I agree with him. That's why I added the documentation to show why \Drupal\rest\Entity\ConfigDependencies::calculateDependencies() does not need to care about either uniqueness or sorting, because that it handled later. It only provides the starting point data.

wim leers’s picture

Issue tags: -php-novice
dawehner’s picture

So can we adapt the testcases instead to be presorted? Its confusing to have a sort there. Its almost like adapting the test for the sake of making it pass ...

wim leers’s picture

StatusFileSize
new1.22 KB
new4.01 KB

I don't think it's like that at all. What we're doing, is a set comparison. So, just made that more clear.

(Copied that over from \Drupal\big_pipe\Tests\BigPipeTest::assertSetsEqual().)

dawehner’s picture

+++ b/core/modules/rest/tests/src/Kernel/Entity/ConfigDependenciesTest.php
@@ -30,10 +30,21 @@ public function testCalculateDependencies(array $configuration) {
+    $this->assertSetsEqual($result['module'], ['basic_auth', 'hal', 'serialization']);

So wait, isn't the order basic_auth, hal, serialization what we expect to see, so in case we don't see that, there is some bug somewhere?

wim leers’s picture

Why would they be ordered alphabetically?

dawehner’s picture

Well, you want some sort of stable ordering, unless you want to kill yourself when dealing with config merges.

wim leers’s picture

Yes, and that's what I explained in #5 in detail, and what @effulgentsia wrote originally: what ConfigDependencies::calculateDependencies() returns is passed into \Drupal\Core\Entity\DependencyTrait::addDependency(), which handles sorting for us.

So this is testing the return value of ConfigDependencies::calculateDependencies(), which does not need to be sorted. Because sorting happens at a later stage.

effulgentsia’s picture

@dawehner: does the docs rewording in this patch help clarify or do you still have concerns?

Also, I changed a newly committed function from #2721595-70: Simplify REST configuration from public to protected in this patch. It should have been to begin with, and was an oversight to have made its way in as public.

I kind of wonder if we should also mark the entire ConfigDependencies class as @internal. Or is there a use-case from someone to use it from outside of RestResourceConfig? If we want to do that, let's do it in a separate issue though.

effulgentsia’s picture

StatusFileSize
new4.57 KB
new1.98 KB

does the docs rewording in this patch

Helps to upload the patch :)

effulgentsia’s picture

+++ b/core/modules/rest/tests/src/Kernel/Entity/ConfigDependenciesTest.php
+  protected function assertSetsEqual(array $a, array $b) {
+    return count($a) == count($b) && !array_diff_assoc($a, $b);
   }

Shouldn't this be array_diff() rather than array_diff_assoc()? Which means, is this function working at all? It doesn't appear to actually assert anything; it only returns a boolean.

dawehner’s picture

@dawehner: does the docs rewording in this patch help clarify or do you still have concerns?

Well, docs don't really help. You have an existing config file, adapt something, like adding hal_json as support, and someone else adds oauth support. Suddenly, due to, as it seems, non deterministic ordering, you maybe get a conflict, even there wasn't really a need for it. This is purely an argument driven by working on real sites. CMI has done a lot of work to ensure that stuff like the field display configuration changes as minimal as possible, so I'm just trying to ensure we have the same here. In order to achieve that, some form of deterministic order would be helpful.

effulgentsia’s picture

In order to achieve that, some form of deterministic order would be helpful.

Right. We do have a deterministic order, so long as the only caller of ConfigDependencies::calculateDependencies() is RestResourceConfig::calculateDependencies(), which is the case in core. Hence my question in #13 of whether we should mark ConfigDependencies as @internal. Does that class have any purpose other than being a helper class to RestResourceConfig?

dawehner’s picture

Thank you @effulgentsia for your explanation.

I kind of wonder if we should also mark the entire ConfigDependencies class as @internal. Or is there a use-case from someone to use it from outside of RestResourceConfig? If we want to do that, let's do it in a separate issue though.

I totally agree with you that this should be an @internal class. IMHO we should do that more often :)

Given that its deterministic, we should, at least IMHO, reflect that in the test case but using exactly the deterministic order of dependencies we expect it to be.

wim leers’s picture

StatusFileSize
new2.22 KB
new5.04 KB
+++ b/core/modules/rest/src/Entity/ConfigDependencies.php
@@ -209,7 +210,7 @@
-  public function onDependencyRemovalForResourceGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
+  protected function onDependencyRemovalForResourceGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {

Yes, +1!

I totally agree with you that this should be an @internal class. IMHO we should do that more often :)

Yes! Did that in this reroll.

Given that its deterministic, we should, at least IMHO, reflect that in the test case but using exactly the deterministic order of dependencies we expect it to be.

It's deterministic, but you still get a different dependency order depending on whether "method" vs "resource" granularity is used. But, if we play with the test cases so that the first method already uses basic_auth, then we can make sure they end up returning the exact same ordered list of dependencies!

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

So the order is in the order of dependencies in the file? That sounds reasonable

wim leers’s picture

Yep :)

alexpott’s picture

  1. +++ b/core/modules/rest/src/Entity/ConfigDependencies.php
    @@ -218,15 +223,19 @@ public function onDependencyRemovalForResourceGranularity(RestResourceConfigInte
    -          if (in_array($format, $rest_config->getFormats('GET'))) {
    +          if (in_array($format, $rest_config->getFormats($first_method))) {
    ...
    -          if (in_array($auth, $rest_config->getAuthenticationProviders('GET'))) {
    +          if (in_array($auth, $rest_config->getAuthenticationProviders($first_method))) {
    

    Whilst doing this let's make the comparison strict for sanity.

  2. +++ b/core/modules/rest/tests/src/Kernel/Entity/ConfigDependenciesTest.php
    @@ -64,11 +64,11 @@ public function providerBasicDependencies() {
    -              'supported_auth' => ['cookie'],
    +              'supported_auth' => ['basic_auth'],
    ...
    -              'supported_auth' => ['basic_auth'],
    +              'supported_auth' => ['cookie'],
    

    Are we fixing a bug?

effulgentsia’s picture

Issue tags: +rc eligible

Discussed with the other committers, and we're ok with this going in any time during 8.2.x, even during RC.

xjm’s picture

Status: Reviewed & tested by the community » Needs work

For #22.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Version: 8.3.x-dev » 8.2.x-dev
Status: Needs work » Reviewed & tested by the community
StatusFileSize
new2.29 KB
new6.09 KB

#22:

  1. Done.
  2. No, we are removing the need for the sort() (see third hunk in patch). This is what @dawehner asked in #7 and later comments.

Only change in this reroll: in_array(X, array) -> in_array(X, array, TRUE — i.e. strict checking. Since that's absolutely trivial, re-RTBC'ing.

Per #23, moving back to 8.2.x.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 26: 2773185-26.patch, failed testing.

wim leers’s picture

Status: Needs work » Reviewed & tested by the community

Apparently the automated re-testing hit a DrupalCI abortion, and hence this was marked NW. The patch did not actually fail. Re-testing.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 713d4e9 to 8.3.x and 90b725c to 8.2.x. Thanks!

  • alexpott committed 713d4e9 on 8.3.x
    Issue #2773185 by Wim Leers, effulgentsia, dawehner: Fix nits in \Drupal...

  • alexpott committed 90b725c on 8.2.x
    Issue #2773185 by Wim Leers, effulgentsia, dawehner: Fix nits in \Drupal...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.