Problem/Motivation

Currently config differ strips (ignores) uuid configuration key on all configuration levels. That's not correct behaviour, since it's normal for some configurations to contain uuid (fe. image styles). Here is one example:

langcode: en
status: true
dependencies: {  }
name: thumbnail
label: 'Thumbnail (100×100)'
effects:
  1cfec298-8620-4749-b100-ccb6c4500779:
    uuid: 1cfec298-8620-4749-b100-ccb6c4500779
    id: image_scale
    weight: 0
    data:
      width: 100
      height: 100
      upscale: false

Proposed resolution

Ignored configuration keys array $ignore = ['uuid', '_core'] should be applied only on root level of configuration.

Here is an example of implementation and it would be good to port it into config_update module.

Remaining tasks

  1. Migrate implementation from update_helper into config_update, or reimplement logic in config_update since it's not so complicated
  2. Write tests

Open questions

  1. Is configuration key ignore for all configuration levels still required?
  2. If not, can we simply use existing constructor argument for root level key ignore list of we have to introduce new parameters?

Comments

mtodor created an issue. See original summary.

jhodgdon’s picture

OK, that seems reasonable... Want to make a patch?

mtodor’s picture

@jhodgdon - Yes, I can provide a patch. Can you just clarify open questions?

I would say, simplest solution:

  1. We don't need indepth ignore list
  2. We can reuse existing constructor parameter ($ignore)
jhodgdon’s picture

Let's see... I think that your conclusion here is correct: removing UUID and _core at all levels of the configuration was a bad implementation decision, so it is a bug that we can fix, and it is OK to change buggy behavior. So I think that we can just change the documentation of the $ignore parameter to the constructor, to say "Config components to ignore at the top level", and leave it at that.

The only caveat I have is that I would want to test this on a real site and verify that with the patch, I don't get a lot more items in the report marked as "changed" because we are no longer stripping out things that are not actually different. But I agree, stripping UUID out of the insides of image styles is not correct, and I'm also pretty sure _code is only added by the config system at the top level.

So... Implementation: Yes, let's just use the existing constructor parameter and change the docs slightly.

It looks like we'd need to add a second argument to the normalize() method, to detect whether we're at the top level or not, because this method is called recursively, and we only want to strip the ignore list at the top level. I would do something like

function normalize($config, $top_level = TRUE)

and then when it is called recursively, put in FALSE.

mtodor’s picture

Status: Active » Needs review
StatusFileSize
new6.41 KB

Here is a proposal.

Following changes are made:

  1. Removing ignore only on Top Level (tests are added)
  2. Configuration keys with empty array are not ignored anymore (and they should not be, if there is no real reason for that?) (tests are added)
  3. There are warnings from phpunit for adding non existing method on TranslationInterface, that's why I changed that part and added testing when NULL value is used, since that's where translatioin is used. It would be good, if someone has better solution? (tests are added)
  4. Using also NULL to test that identical comparason is used. (tests are added)
jhodgdon’s picture

Status: Needs review » Needs work

The patch looks mostly good to me, and our tests passed, so that is also good. I'm assuming you tested it on your case?

One thing needs a quick fix:

+  /**
+   * Recursively sort an array by key.
+   *
+   * @param array $array
+   *   An array that should be sorted by keys.
+   *
+   * @return array
+   *   Returns a sorted array by keys.
+   */

I'll fix up that method documentation, just pasting it here to remind myself to do that.

Another question I have is what these additions to the test are:

@@ -201,6 +251,7 @@ class ConfigDifferTest extends UnitTestCase {
       'id_to_remove' => 'test.remove.id',
       'type' => 'old_type',
       'true_value' => TRUE,
+      'null_value' => NULL,
       'nested_array' => [
         'flat_array' => [
           'value2',
@@ -216,6 +267,7 @@ class ConfigDifferTest extends UnitTestCase {
       'id' => 'test.config.id',
       'type' => 'new_type',
       'true_value' => FALSE,
+      'null_value' => FALSE,
       'nested_array' => [
         'flat_array' => [
           'value2',
@@ -284,11 +336,13 @@ class ConfigDifferTest extends UnitTestCase {
           'orig' => [
             'nested_array::flat_array::1 : value1',
             'nested_array::flat_array::2 : value3',
+            'null_value : (NULL)',
             'true_value : 1',
             'type : old_type',
           ],
           'closing' => [
             'nested_array::flat_array::1 : value3',
+            'null_value : ',
             'true_value : ',
             'type : new_type',
           ],

It seems like those might have been things you tried but decided you didn't need? Thanks!

jhodgdon’s picture

Oh, it looks like the weekly test of this module is failing, that is the t() thing you fixed. I created a separate issue for that, so let's take the fix for that out of this patch and put it on
#2961886: Unit test for Config Differ is failing

mtodor’s picture

@jhodgdon thank you for review.

I have tested integration with our module and I removed overwrites in our class for (normalize, same and diff) and tests are passing. So it looks good.

Regarding part of the code with added null_value - it is needed. It is relevant because, translation is used only for NULL value. If you take a look at this part of code:

+++ b/tests/src/Unit/ConfigDifferTest.php
@@ -32,10 +32,11 @@ class ConfigDifferTest extends UnitTestCase {
     $this->stringTranslation
-      ->method('t')
-      ->will($this->returnArgument(0));
+      ->method('translateString')
+      ->willReturn('(NULL)');

You will notice that I have changed code for mockup of translateString to always return (NULL) and that's executed (or used) by testDiff(). With that we are testing that formatting of NULL values is working properly.

That's what I have mentioned at #5-3. If someone has a better proposal?

P.S. Since you have merged #2961886: Unit test for Config Differ is failing, you have to take care when you merge this patch so that you don't lose the relevant part for translation.

jhodgdon’s picture

Status: Needs work » Needs review
StatusFileSize
new5.58 KB
new7.75 KB

So, I looked at this some more.

This patch is doing more than what the issue summary says. Normalization used to remove empty arrays from config, and this patch reverses that. This is not a good idea, based on my manual testing when I was first writing this module. It was leading to a lot of false positives, where I had installed a site a while back, and since then some new component had been added to the config, but it was empty. There was no functional difference between the config in my system and the new config, hence I didn't want it to show up in my reports as being changed when there was no real change.

So, I've put that back, and changed the test accordingly. Also updated the documentation mentioned in #6. The test passes locally... let's see what the bot says about all the tests.

Status: Needs review » Needs work

The last submitted patch, 9: 2959072-9.patch, failed testing. View results

jhodgdon’s picture

Status: Needs work » Needs review
StatusFileSize
new1.21 KB
new8.84 KB

Fix test failure... hopefully! Odd, this didn't fail locally for me but anyway, I understand the problem.

jhodgdon’s picture

StatusFileSize
new8.84 KB
new979 bytes

Passes this time, but two little coding standards fixes...

mtodor’s picture

Nice progress! I think we are getting somewhere.

I have checked the code and it looks clean.

  1. +++ b/src/ConfigDiffer.php
    @@ -80,27 +80,40 @@ class ConfigDiffer implements ConfigDiffInterface {
    +  protected function normalizeArray(array $array) {
    ...
    +        $new = $this->normalizearray($value);
    

    I guess this should be $new = $this->normalizeArray($value);.

  2. +++ b/src/ConfigDiffer.php
    @@ -80,27 +80,40 @@ class ConfigDiffer implements ConfigDiffInterface {
    +          unset($array[$key]);
    

    Well, I'm not sure about this. I'm really against removing of key, no mather if it's empty or not. I'm always looking from perspective of diffing. And to have config like this:

    key:
      2: 'abc'
    

    to be same as this one:

    key:
      - {  }
      - {  }
      - 'abc'
    

    is not good solution for me. :(

  3. +++ b/tests/src/Unit/ConfigDifferTest.php
    @@ -35,12 +36,25 @@ class ConfigDifferTest extends UnitTestCase {
    +      ->will($this->returnCallback([$this, 'mockTranslate']));
    

    Nice solution!

jhodgdon’s picture

That is a good point about empty arrays, and thanks for catching my syntax error!

So, let's think about this question of arrays.
- It seems like empty arrays should *not* be removed if they are in numerically-sequenced config.
- I still think empty arrays should be removed if they are in keyed config, to avoid meaningless config differences.

Let's look at an example:

a: 1
b: {}
c:
  - one
  - two
  - {}
  - four
d: something

In this example, I think we should remove b, because it has no values in it. But we shouldn't remove the empty array under the C sequence, because that would break the sequence numbering.

This points to the fact that we don't have any tests that include numeric sequences. So... let's see if we can put this example into the test (or something substantially similar) and make it work. Thoughts?

alexpott’s picture

It's tricky. The problem is that this module doesn't decide the meaning of configuration. Different implementations do. And it is possible for an empty array to mean something different from not being set. It wouldn't be great for the the user but it is totally possible and probably that someone has configured something to work that way. The same unfortunately goes for order of configuration. Ideally it'd never make a difference or we'd be enforcing orders via schema (there are patches to do this). But there are definitely examples in the wild were the order matters.

jhodgdon’s picture

OK, I would be willing to get rid of the "remove empty arrays" thing.

But are there actually examples of config where the order of the keys, at some level, in a mapping (not a sequence) matters? I don't even know if that is possible to retain, given that there is a schema, and if you save config, it gets saved in the order of the schema.

jhodgdon’s picture

The problem is that if you don't normalize the config at all, you end up seeing a bunch of "THIS CONFIG CHANGED" entries in your config update reports. Most of them are not real differences, they're just that you imported config a while back, and since then the schema has changed, and hopefully the module developer has also updated their stored config accordingly, so it has a bunch of minor changes that don't affect your actual site behavior. Like adding new options that have sensible (empty) defaults, and reordering things.

So the point of the normalization is to omit the things that don't matter, so you can see what has actually changed. I think it should cover the 99% case, so that the report is pretty good at telling you if there have been substantive changes that you should be aware of. If some module is doing something weird and unstable with config, I don't think we need to cover that rare and probably ill-advised possibility. But something like removing empty arrays in the middle of a sequence, we shouldn't do.

So with that in mind, ... I think I'm still in favor of (a) reordering config and (b) removing empty arrays except in sequences, because most of the time the reason the active config item has nothing and the one in config/install has an empty array is that there's a new key added to the schema since you installed, and it has a sensible (empty) default. Your site is working, after all, so there must have been a default... and if there was some non-empty data in there, it would show up in the diff, and if the active config needed to have that empty array in there, there would have been a hook_update_N() to add it.

mtodor’s picture

I would suggest that we make followup ticket that will solve "remove empty arrays" problematics and just finish what we have started here. There is no point keeping things back because of other no related stuff.

So last required change is $new = $this->normalizearray($value); in camel case and we are fine.

  • jhodgdon committed 1fd2a99 on 8.x-1.x
    Issue #2959072 by jhodgdon, mtodor: Normalization should not strip uuid...
jhodgdon’s picture

Status: Needs review » Fixed

Good idea. So, I committed the change (with the syntax fix). I created a child issue #2962529: Figure out normalization for ConfigDiffer as well as a change notice for this change.
https://www.drupal.org/node/2962527

Thanks for the comments and patches!

Status: Fixed » Closed (fixed)

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