Suggested commit message: Issue #2293063 by reyero, Gábor Hojtsy, chx, Wim Leers: Config schema mapping parsing is inconsistently forgiving, does not conform to the interface

Problem/Motivation

This came up in #2277945: Typed configuration implements TypedDataInterface improperly, causes problems debugging schemas in #2266427: Add more info on schema validity to configuration inspector for debugging and is just not consistent.

1. If you define a type as type: boo and the boo type does not exist, it will fall back on an undefined type. If you define the type as type: boo.[type] (where type is foo) and there is no specific type defined for boo.foo and there is no boo.*, the type will fall back on undefined. This is true for each individual element, sequence elements, etc. So the type definitions are very resilient when it comes to errors. Except if you forget/miss to define a key on a mapping, all hell breaks loose and you cannot iterate into that element in the mapping or request elements under it with the schema. This is obscured for the schema validation and value casting use cases because it swallows up the exceptions thrown. But this hides several real problems. Compared with how resilient everything else is, this is totally not expected.

2. We made up our own SchemaIncompleteException and even use it for cases when you try to access a key on a mapping that is not there (so why on earth would you have a schema for it?). The interface documents that an InvalidArgumentException should be thrown in this case, so we should do that instead of deviating from what the interface says is happening.

3. Once we do this, several things start to fail because now actual failures are surfacing where an item was not defined as an array but is attempted to be used as such. We need to fix these fails because they are actual schema mismatches. With those concrete issues fixed, the new system is more resilient to undefined items and more strict for how defined elements enforce their type and fail on issues instead of hiding these real errors.

Proposed resolution

1. Use the same fallback mechanism for types on undefined mapping elements that is on for sequence and other types.
2. Instead of making up our own exception (and then misusing it) only for undefined mapping keys but not for anything else, use the exception expected according to the interface we implement.
3. Fix the resulting fails.
4. Add testing foe the fixed mapping behaviour.

Remaining tasks

Tests. Review. Commit.

User interface changes

None.

API changes

Instead of throwing exceptions on keys if their schema is not defined, Mappings will be more resilient in this case. For nonexistent keys, it will throw the expected InvalidArgumentException.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Gábor Hojtsy’s picture

Title: Config schema mapping parsing is inconsistently forgiving vs. all other types » Config schema mapping parsing is inconsistently forgiving, does not conform to the interface
Issue summary: View changes
Status: Active » Needs review
FileSize
7.52 KB

First patch based on extract of what @reyero came up with earlier in #2277945: Typed configuration implements TypedDataInterface improperly but was descoped there. Not tested :D

Gábor Hojtsy’s picture

Issue summary: View changes
Gábor Hojtsy’s picture

#2293105: MigrateActionConfigSchemaTest duplicates all its code from SchemaCheckTestTrait removes/reworks the migrate test base as well in favour of needing to duplicate code.

Status: Needs review » Needs work

The last submitted patch, 1: 2293063-mapping-resilient-and-interface-fix-1.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
2.51 KB
8.84 KB

Schema trait test fixed. Ran some more tests locally and those passed.

Note also (in interdiff) that this now also fixes inconsistent error reporting in the schema check as a nice side effect. Before, if a non-array type element was found, it said it is DEFINED as a non-array type but is an array. Now if it is not defined it will say schema missing (correct error) instead of saying how is it not well defined (incorrect error, since its not even defined).

Gábor Hojtsy’s picture

Issue tags: +Needs tests

Also needs tests for itself, that is basically that we can access non-defined mapping items through the schema.

Status: Needs review » Needs work

The last submitted patch, 5: 2293063-mapping-resilient-and-interface-fix-5.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
11.1 KB
2.25 KB

All the fails are legit mismatches of schema vs. data structure, yay. Swallowing the exceptions before did hide these issues. Yayay. Includes the following fixes:

- LinkFieldTest.php used url_only wrong, as an array of booleans even though its a boolean; see that url_only is a single boolean checkbox in LinkFormatter
- system info schema mismatch in update_test, the settings are a sequence of sequences, not just one sequence, see update_test_system_info_alter()
- views access by role is a list of roles under a single container key named role, see the access/Role.php class in views

There are further fails that need fixing but this should fix a lot.

Status: Needs review » Needs work

The last submitted patch, 8: 2293063-mapping-resilient-and-interface-fix-8.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
11.62 KB
532 bytes

Woah, also looked at the field SQL fail. The problem is with the indexes schema. Unfortunately the indexes may be an array or a string. The schema says its a string, which is correct for SOME cases, but not all. Eg. FeedStorage.php has this snippet showing both:

    $schema['aggregator_feed']['indexes'] += array(
      'aggregator_feed__url'  => array(array('url', 255)),
      'aggregator_feed__queued' => array('queued'),
    );

There is no data we can inspect to figure out if its a mapping of 0: name (string), 1: length (integer) or a string. So we need to use the much dreaded ignore type here to be correct :/ (Or change the data structure, which I don't want to do here).

Still several ones to figure out. Its a hunt.

Gábor Hojtsy’s picture

Fixing the contrib update test too. This one has a test module with a schema but needs the second fix in this issue :D The update_status config it has is not a sequence of strings but instead a sequence of mappings with module status information. Makes that one passing too.

Gábor Hojtsy’s picture

I have a really hard time figuring out the filter/ck fails, all of which seems to come down to how allowed values are stored for HTML formats. I could not find the matching schemas at all to the data so far... If someone can help with that that would be great. I'll stop for today and pick it up tomorrow.

The last submitted patch, 10: 2293063-mapping-resilient-and-interface-fix-9.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 11: 2293063-mapping-resilient-and-interface-fix-11.patch, failed testing.

The last submitted patch, 1: 2293063-mapping-resilient-and-interface-fix-1.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
10.33 KB

Status: Needs review » Needs work

The last submitted patch, 16: 2293063-mapping-resilient-and-interface-fix-15.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
13.09 KB
2.76 KB

Update fixing two more issues:

- ConfigEntityQueryTest was using the 'array' key in the config test entity as a two level array and it was not defined as such, now defined like that :)

- Drupal 6 field instance migrations migrated vocabulary and parent settings to field instances where they have no business to be... they should be on the field itself, where they already are; thanks @chx for helping me figure this one out

+1: I debugged and found the cause for the options field fail as well, but that is somewhat complicated :/ It is about float type fields with allowed values. For these, you have allowed values as follows on the UI:

0.5|Small value
1.5|Bigger value

This is stored in a sequence keyed by the float and the value casting uses the array keys to access elements. So this becomes settings.allowed_values.0.5 which is a BIG no-no, since it is understood by the config/schema system as two levels of elements under allowed_values, which is not the case. We need to figure out how to refer to those elements or figure out something else for the value casting, eg. get elements individually instead of their full dotted path. Will try to summon @alexpott for this unless I come up with something useful and reasonably easy myself...

Gábor Hojtsy’s picture

With proper tag field setup in the migrate test.

The last submitted patch, 18: 2293063-mapping-resilient-and-interface-fix-19.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 19: 2293063-mapping-resilient-and-interface-fix-19-v2.patch, failed testing.

Gábor Hojtsy’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
982 bytes
14.76 KB

So the CK and the FilterAPITest issue was that there is a test-only FilterTestRestrictTagsAndAttributes filter defined but no settings schema, so the default filter_settings.* schema applied which would be a sequence of strings. That does not apply to this mapping/array thing. Unfortunately there are settings here that cannot be described again with schema, they may be either array or bool. So used the ignore type again. Heh.

Thanks @WimLeers for helping me figure this one out. Added a suggested commit message now that there is so much external info on this :)

This should be down to the float (optionuitest) and exception (configcrudtest) fails.

Status: Needs review » Needs work

The last submitted patch, 22: 2293063-mapping-resilient-and-interface-fix-22.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
15.84 KB
1.75 KB

Fixing the config crud test based on discussion with @alexpott. This is another existing bug in core that the new behaviour exposed :D Yeah. So if you tried to set an unsupported type to an existing element that had an undefined type (eg. type: bunny) then that would totally go through. An easy fix is to do the basic type validation even for undefined/ignore types, those should not be able to store resources and other wild types that are not arrays or scalars. This is ensures for other types in castValue() and validateValue(). Also improving docs on validateValue().

This should only leave the float option element which will need to reformulate how field allowed options are stored. Meh.

Gábor Hojtsy’s picture

So the final one is the +1 problem explained in #18. Discussed that with @alexpott too, and since the format of the data stored is fundamentally going against config principles, the config data format itself needs fixing. Looking at the code for allowed_values this is a very far reaching change and changes fundamental parts of the field structure so may be beta blocking in its own right (but IMHO at least critical). So opened #2293773: Field allowed values use dots in key names - not allowed in config as a separate issue for it.

For this one the best we can do for now is we type it as ignore instead of a sequence, which will ignore the whole thing. This should be reworked in #2293773: Field allowed values use dots in key names - not allowed in config entirely, this is just a stop-gap. Especially because for it to be translatable, we NEED it to not be ignored. We need default option values to be translatable because they show up as UI labels.

Jose Reyero’s picture

Yes, this looks great and makes a lot of sense.

Gábor Hojtsy’s picture

Crosspost I think? Reuploading patch.

The last submitted patch, 24: 2293063-mapping-resilient-and-interface-fix-24.patch, failed testing.

Gábor Hojtsy’s picture

Issue summary: View changes
Gábor Hojtsy’s picture

Now with test for what this does in itself. Makes items on mappings that are not defined be undefined instead of fail hard. The test only patch is the interdiff in one :)

Status: Needs review » Needs work

The last submitted patch, 30: interdiff-and-test-only-patch-in-one.patch, failed testing.

Gábor Hojtsy’s picture

Priority: Normal » Major

This fixes bugs in migrations, views access with roles, link field testing, etc. so elevating to major.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
Dries’s picture

Status: Needs review » Reviewed & tested by the community

I looked at this patch, talked about it with Gabor, and it looks good to go to me. Marking it RTBC. Maybe someone else can commit it, or I can commit early next week.

Gábor Hojtsy’s picture

Assigned: Unassigned » alexpott

Best person to final blessing would be alexpott :)

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Nice - new tests and consistency.

Committed da5ea41 and pushed to 8.x. Thanks!

  • alexpott committed da5ea41 on 8.x
    Issue #2293063 by Gábor Hojtsy: Fixed Config schema mapping parsing is...
Gábor Hojtsy’s picture

Issue tags: -sprint

Yay, this now allows debugging of missing schema item definitions in config inspector immediately :)

Opened #2295737: Not all shipped configuration passes validation even with all modules enabled with the issues it finds in the standard schema.

webchick’s picture

Great work, folks! :)

Status: Fixed » Closed (fixed)

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