Problem/Motivation
- How to know when a value may be the empty string but not NULL, or the empty array but not NULL?
-
Quoting @bircher, maintainer of https://www.drupal.org/project/config_split, https://www.drupal.org/project/config_filter, etc., after I asked him
What concrete things have gone wrong due to bugs in configuration, especially if they were caused by overriding/splitting/… — any of the advanced Configuration Management things?
what can go wrong, well sorting mostly... oh and to know if a key is required or not. So sometimes when a value is null or the array empty we can omit the key and sometimes not
👉 This issue aims to address the "sometimes when a value is null or the array empty" part. See #3364108: Configuration schema & required keys for the other part.
- All Symfony validation constraints return early if a value is NULL
-
Quoting
\Symfony\Component\Validator\Constraints\ChoiceValidator::validate():if (null === $value) { return; }\Symfony\Component\Validator\Constraints\RangeValidator::validate(): same thing.\Symfony\Component\Validator\Constraints\LengthValidator::validate(): same thing.- …
… which makes sense: if the value is optional, it is
NULL, and in that case, none of these validation constraints need to run anymore.That means that:
machine_name: type: string label: 'Machine name' constraints: Regex: pattern: '/^[a-z0-9_]+$/' message: "The %value machine name is not valid." Length: # @see \Drupal\Core\Config\Entity\ConfigEntityStorage::MAX_ID_LENGTH max: 166can easily be bypassed … by just specifying
NULL! 😱The only solution today is to specify
NotNullexplicitly inconstraintstoo, as the first one. But that only works as long as a config schema does not extend/reuse a type, because it is not possible to unset constraints from a parent type.Imagine we had hardened
type: machine_namelike this in Drupal core:machine_name: … constraints: NotNull: … Regex: … Length: …then nothing would be able to use
type: machine_nameand make it optional. They'd have to duplicate the entire type definition!👉 This issue will ensure that any
constraints: { … }added to config schema definitions will actually get executed.
The following things in Drupal core's config schema are using nullable: true:
entity_reference_selection.default:target_bundles(which istype: sequence) since #1978714: Entity reference doesn't update its field settings when referenced entity bundles are deleted in 2015editor.editor.*:image_upload.max_dimensions.widthandeditor.editor.*:image_upload.max_dimensions.height(both of which aretype: integer) since #2644838: Add test coverage for the editor's max dimension setting, and fix its message when one dimension is unrestricted in 2016views.display.page:use_admin_theme(which istype: boolean) since #2719797: New option for Views page displays to use the admin theme in 2023.- … and several more since this issue was opened:
theme_settings:logo.url,media.settings:iframe_domainandupdate.settings:fetch.url(since #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate)
It's that first one that introduced nullable: true support in the first place. It also added this to \Drupal\Core\Config\Schema\SchemaCheckTrait::checkValue():
// Array elements can also opt-in for allowing a NULL value.
elseif ($element instanceof ArrayElement && $element->isNullable() && $value === NULL) {
$success = TRUE;
}
\Drupal\Core\Config\Schema\SchemaCheckTrait::checkValue() already allowed NULL on all primitive types (integer, string, etc.).
However, despite the presence of
// Add the NotNull constraint for required data.
if ($definition->isRequired()) {
$constraints['NotNull'] = [];
}
in \Drupal\Core\TypedData\TypedDataManager::getDefaultConstraints(), no validation constraint violations are generated when calling
$typed_config->createFromNameAndData($config_name, $config_data)
->validate()
This would enable modules like https://www.drupal.org/project/config_split, https://www.drupal.org/project/config_filter et cetera to work more reliably.
Steps to reproduce
See included test coverage.
Proposed resolution
- ⚠️ DO NOT CHANGE ANY BEHAVIOR FOR EXISTING CODE/CONFIG SCHEMAS! ⚠️ That is the mistake this issue was making until #46. All the points below apply ONLY to config schema type definitions that have
constraints: FullyValidatable: ~.
In this issue,
only a single typethree types get that (the one below,system.menu.*andshortcut.set.*, because these already have validation constraints for everything):config_test.types.fully_validatable: type: config_test.types constraints: FullyValidatable: ~ - Assume
nullable: falseby default, and make\Drupal\Core\Config\TypedConfigManager::buildDataDefinition()mark the built data definition as required by default, unless a config schema type specifiesnullable: true, in which case it will be marked optional. - Add test coverage to prove that when
nullable: trueis present, setting a value ofNULLis considered valid, for all data types. And if it's not set, a value ofNULLshould trigger a validation error. - Add test coverage to prove that simple config is not affected until they opt in: see
SchemaCheckTraitTest. - Add test coverage to prove that config entity types are not affected until they opt in: see
\Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase::testRequiredPropertyValuesMissing()
Consequences
- This has zero effect on contributed modules' config, because A) config schema type definitions must opt in, B) config validation does not yet run for them (not in tests, not in production, since #3361423: [meta] Make config schema checking something that can be ignored when testing contrib modules.
- This has zero effect on core modules' config, because A) config schema type definitions must opt in.
- The only way this can affect Drupal core/contrib in production: since #3364506: Add optional validation constraint support to ConfigFormBase, simple configuration forms that were updated to not use form-based validation, but config schema-based validation, will be affected, if the simple config they're editing (e.g.
update.settings,system.site…) have theFullyValidatableconstraint. This issue does not add it to any config schema types in Drupal core. Simple config forms already using do run validation constraints, but sincesetRequired()will never be called, theNotNullconstraint validator will never run, and hence this does not cause any behavioral changes.
Remaining tasks
Review.
User interface changes
None.
API changes
None.
Data model changes
None.
Release notes snippet
N/A
| Comment | File | Size | Author |
|---|---|---|---|
| #76 | Screenshot 2024-03-19 at 11.15.12.png | 316.03 KB | 2dareis2do |
| #74 | array_null_config_fatal_errort_fix_10_2.diff | 30.61 KB | 2dareis2do |
| #40 | 4092-test.patch | 22.45 KB | effulgentsia |
| #34 | 3364109-34.patch | 32.65 KB | effulgentsia |
Issue fork drupal-3364109
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
wim leersComment #4
wim leersThe commits above:
config_test.*) that exists precisely for testing purposes → ✅config_test.*config → ❌ fails due to a bug in the existing validation logic forconfig_test.validationthat was introduced in #1928868: Typed config incorrectly implements Typed Data interfaces:$config_data['boolean'] = [];and an array is obviously not a boolean, so now\Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidatorfails, because validation is actually enabled! That looks like this:PrimitiveTypeconstraint violation → ✅Alright, now we have a working baseline to continue our work, to actually implement the proposed plan 👍
Comment #5
wim leersThe commits above:
→ ❌ this should cause a test failure:
[dependencies] This value should not be null.is something thatconfig_test.dynamic.dotted.default(which istype: config_test.dynamic.*.*, which extendstype: config_entity) inherited fromtype: config_entity.In other words:
dependencieshas been missing all along from that configuration entity, because configuration entities must ALWAYS have their dependencies defined. This is easily remedied though: we just updatecore/modules/config/tests/config_test/config/install/config_test.dynamic.dotted.default.ymlto containdependencies: {}. → ❌ still, with NO effect whatsoever, still the exact same exception!? 🤪🤯NotNullConstraintValidatordoes not work correctly for config schema sequences and mappings. 😱 It does:… which makes sense for lists & complex data in a entity fields context, but not in a config schema context.
SequenceandMappingextendArrayElement, which in turns implementsComplexDataInterface. That's why this code is causingdependencies: {}to be remapped todependencies: null.A tweak to the if-test and a helpful comment later → ❌ as expected, but now only for the top-level keys that are defined by
config_test.schema.yml:👍
So it's not yet working, but we discovered a hard-blocking bug related to validation of required values and that's now fixed. 👍
Comment #6
wim leersThe commits above:
Ah, yes,
\Drupal\Core\Config\Schema\SchemaCheckTrait::checkValue()also still generates an error for the mismatch. If/when we eventually reach 100% config validation, we'll be able to remove these. But for now, we should expect these too.(Every piece of configuration that reaches 100% validatability will be able to adopt config validation for use in its admin UI; schema checking's storage type checking alone is not enough for those UIs: that's where validation comes in.)
Comment #7
wim leersThe failures in
BaseFieldOverrideValidationTest,ContactFormValidationTest, et cetera are all inConfigEntityValidationTestBasesubclasses, which were all added in #3324150: Add validation constraints to config_entity.dependencies. They explicitly perform validation and are the only things in Drupal core to do so, precisely to help Drupal core get to a place where all config entities are 100% validatable.But now more things are marked as required, so we get a lot more
This value should not be null.errors. Unfortunately, those failures are currently a PITA to debug … and to fix that, #3349293: Make assertions using ConfigEntityValidationTestBase::assertValidationErrors() clearer already exists (and has been RTBC for 3 weeks). I've applied that patch locally to be able to make sense of the otherwise impossible to decypher failing assertions.For each of the values that were not allowed to be
NULL(and actually, all of them were really just missing required keys, but that's for #3364108: Configuration schema & required keys to solve, not this issue!), I went with the most minimal value possible, because they would almost all benefit from explicit validation constraints in the future:type: integer→0type: string→''This should now be green!
Comment #8
wim leersYay, #3349293: Make assertions using ConfigEntityValidationTestBase::assertValidationErrors() clearer landed just now!
Comment #9
bircherlooks really nice already! due to the relatively low amount of nullables I didn't write any special code for it yet in config split and I will wait for the other issue to be done before changing it there.
Comment #10
bircherbtw I love your comments here. I am wondering is not more of this insight could be added as comments somewhere. i went through a similar process when working on config split and getting into the weeds of the config schema.
Comment #11
wim leersA lot has changed sinceI worked on this 2 months ago today!
nullable: truecases.type: mappingbehave like everyone (including core committers!) already thought it worked.I'll process and address all feedback here 👍
Comment #12
wim leersA gazillion failures as expected! 😄
Why now and not before? Because before, I was specifically only testing one piece of config. But with #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate now committed, as well as several of its follow-ups, we're actively working towards getting all of Drupal core's config to comply with the validation constraints. So the same is true here now.
Turns out that a whole range of problems can be solved quite easily: for example
\Drupal\taxonomy\Entity\Vocabulary::$weightis required (because it does NOT havenullable: trueintaxonomy.schema.yml), but it has a default value of0. That's why that does not trigger thousands of validation errors.But … the same is not true for
Role::$weight! 😅 So simply settingRole::$weight = 0;solves a lot of validation errors, plus it's entirely reasonable.Another example of a pattern:
NodeType::$description,MediaType::$description,Vocabulary::$descriptionare all treated optional by the entirety of the current codebase, plus it's not essential for usability, so the pragmatic solution is to simply mark these asnullable: true.The batch of commits I just pushed should bring us from 3,111 failures to hopefully far fewer.
Comment #13
wim leersYay, #3362457: Fix all PluginExistsConstraint constraint violations in tests and #3377030: Add validation constraint to `type: label`: disallow multiple lines just landed — rebased! 👍
Comment #14
wim leersThat got us from 3,111 to 1,450 👍
Here's another big push.
Next steps:
ConfigEntityValidationTestBasesubclasses. (Which are also kernel tests.)Comment #15
wim leersThe sheer number of specific failures means that fixing all invalid config or inaccurate config schema in this issue would lead to an enormous MR.
So rather than doing it all here, I propose to adopt the same approach as #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate: allow ignoring patterns (there: violation constraint messages, here: specific property paths in specific config), to enable the bulk of the work to be deferred to follow-up issues. We have a good track record on that front: all 4 follow-up issues of #3361534 have been fixed in the subsequent week! 😊
I focused on one specific test here to determine what the necessary infrastructure is to make that possible:
ResolvedLibraryDefinitionsFilesMatchTest. It may cause some additional tests to pass, but that's the only one I was targeting here.To be continued.
Comment #16
wim leersAs I said in #15, making all necessary changes in this MR would lead to an untenable scope. So I'm going back to the roots of this issue:
… which is now far more true than before, thanks to this MR adding
\Drupal\Core\Config\Schema\SchemaCheckTrait::$ignoredPropertyPathsand::isViolationForIgnoredPropertyPath(), which clearly prove that the infrastructure change is working because there were thousands upon thousands of test failures 😁🤓Updated issue summary, and marking this as blocking #3361423: [meta] Make config schema checking something that can be ignored when testing contrib modules.
Comment #17
wim leersThat's working … except that we're now ignoring all violation messages for any given property path, but multiple different ones may exist for the same property path!
For example right now: a label is required (so NULL is not allowed, only strings), but not multi-line strings.
So the
SchemaCheckTrait::$ignoredPropertyPathsinfrastructure should only ignore the specified validation error messages at the specified property paths in the specified config 😅🤓Just pushed a commit that does that. Now we should finally be green! (At which point I'll run the entire test suite again, at last.)
Comment #18
wim leersOnly 35 failures in functional tests + a few failing
Nightwatchtests! 👍 This should bring it down to zero, or very close to zero.… but this is now definitely in the territory! 😊
Comment #19
borisson_This issue is a lot bigger then it could be, because it also includes the setup for validating the config schema's in the tests and making sure only the known things are passing.
After this one is done, the follow up issues after this one should be a lot smaller.
Comment #20
wim leersActually, it's smaller thanks to that infrastructure 😅 If we'd have had to fix everything here, this would have been a megabyte-sized unreviewable monster I think 🙈
Anyway, glad you like it — I'll start creating follow-up issues 👍
Comment #21
bircherBeautiful!
RTBC+1
Yes we add a lot of things to ignore.. but that is much more manageable than fixing everything here. I am curious how much of the contrib tests will be affected by this. I would assume that contrib schema and test config are equally out of whack. So I think how to address this by expanding the ignore rule would be great to document in a change record. Of course fixing it would be preferable.. but maybe we can still make it easier for tests not to fail.
Comment #22
wim leersYep, the ability for contrib to opt out will happen in #3361423: [meta] Make config schema checking something that can be ignored when testing contrib modules!
I'll still:
Comment #23
wim leersactionable now because once this issue lands, 100% ofnope, not even this one: #3379731-2: Fix NodeType config entity type config schema violations: name, description, helpnode.type.*is validatable! 👍Comment #24
borisson_No I agree, creating all of these is useless if it means we have to postpone all of them because we need to add validation to more types still. I think we should keep this is as is for now.
Comment #25
wim leersThis is now also blocking #3379091 — see #3379091-13: Make NodeType config entities fully validatable and #3379091-14: Make NodeType config entities fully validatable.
Comment #27
wim leersPer @lauriii 👍😊
Comment #28
lauriiiI have reviewed the changes earlier this week when I rebased the MR. I didn't have specific concerns on this change and it makes sense that we are marking all non-nullable elements as required. However, I felt that this should at least get at least one review from a subsystem maintainer or a framework manager, given that it is a fairly significant change. I think it would be fine for a framework manager to sign-off on this as well given that the sole Configuration API subsystem maintainer is on leave. 😊
Comment #29
wim leersMerged in upstream, trivial conflict 👍
Comment #30
lauriii"Custom Commands Failed" after the rebase 😞
Comment #31
wim leersFixed.
Comment #32
borisson_back to rtbc.
Comment #33
effulgentsia commentedThis MR is quite a bit behind HEAD and needs a rebase.
Comment #34
effulgentsia commentedI tried rebasing this MR, but ran into trouble because #29 was a merge rather than a rebase. Doing another merge of 11.x only turns up one conflict that's easy to resolve, but I'm hesitant to push that in case it would be better to redo the MR as a rebase.
In the meantime, here's a patch, which I'll use for reviewing until the MR is in a mergeable state. Setting to NR to run tests.
Comment #36
wim leers#34 Thanks for that careful consideration! Because I've experienced old links to MR commits breaking when rebasing (and those old links to commits are linked here on the d.o issue), I've been favoring merging in upstream rather than rebasing. At least for more complex issues like this one — if it's a simple commit history, then I'll just do a rebase since it won't be hard to find the right commit by hand when reviewing.
Thanks to #34 I knew I had to not just merge in upstream, but also to adjust the test that was just added in #3382580: Add new `ImmutableProperties` constraint ever so slightly. Since it's such a trivial change (just respecting the ignored property paths we've had in HEAD for a while), re-RTBC'ing.
Can't wait to read your review! 😊
Comment #37
effulgentsia commentedI stared reviewing this MR but it's going to take me some time to wrap my head around it. However, the first thought that jumps out at me is that the proposed resolution says:
Do we not validate config during a config import though? If we do, then I would consider this as affecting production (in the sense that people deploy config to production), not just tests.
Comment #38
effulgentsia commentedI'm still processing the implications of this MR. In particular:
Seems like potentially quite a disruptive change. It also conceptually conflicts with
SchemaCheckTrait's current logic to allow NULL for all primitive types.If the goal of this MR is in fact to change Drupal's design to no longer allow NULL for primitive types unless they've explicitly set
nullable: truein their schema, then this MR should also change that part ofSchemaCheckTrait, so "Needs work" for that. Meanwhile, I'm still mulling over whether such a design change is appropriate. I do appreciate the benefit of more strictness, but I'm also concerned about how to manage the BC implications.In addition to config validation potentially running during a config import per #37, config validation now also runs in
ConfigFormBasethanks to #3364506: Add optional validation constraint support to ConfigFormBase, so tagging this for an issue summary update.Comment #39
effulgentsia commentedSomething that makes this MR potentially not too disruptive is that the config validators, including the NotNull validator, only run for keys that exist in the data being validated. So the
setRequired()referenced in #38 isn't actually making the keys required, it's only making sure that the values aren't explicitly set to NULL. I'd expect there to be fewer cases of sites setting values of config keys explicitly to NULL than there are of sites missing config keys entirely.However, given that, I don't think this MR should call
setRequired(). We already haveisNullable()forArrayElement; what about adding that to the other types that we need it for and base theNotNullconstraint on that?At some point, we might also want to trigger violations for when keys are missing (which I'm guessing is what #3364108: Configuration schema & required keys wants to do?), and it's at the point when we do that that I think we should use
setRequired().Comment #40
effulgentsia commentedAlso, to understand #39 more, I want to see what tests fail if we take out the MR's changes to
SchemaCheckTrait, so here's a patch to probe that.Comment #41
effulgentsia commentedAh, so this turns out to only be true for simple config objects. But for config entities, the
config_exportkey in the@ConfigEntityTypeannotation fills in NULL values for keys that are missing, so there's no distinction. Given that, the use ofsetRequired()in this MR makes sense.However, in that case it seems like the scope of this issue isn't captured in the issue title. Seems like the issue title should be more like, "Make all config keys required by default unless they're explicitly allowed to be nullable"?
Comment #42
wim leers#37:
Not really.
\Drupal\Core\Config\ConfigImporter::validate()triggers\Drupal\Core\Config\ConfigEvents::IMPORT_VALIDATE, but that does not run any actual validation at all. There's just a handful of things that are validated by custom event subscribers:\Drupal\system\SystemConfigSubscriber::onConfigImporterValidateNotEmpty()(prevent empty config from deleting everything!)\Drupal\system\SystemConfigSubscriber::onConfigImporterValidateSiteUUID()(site UUID must match)\Drupal\Core\Entity\Event\BundleConfigImportValidate::onConfigImporterValidate()(bundles being deleted in this import must not be in use)\Drupal\Core\EventSubscriber\ConfigImportSubscriber::onConfigImporterValidate()(config names must be valid)\Drupal\config\ConfigSubscriber::onConfigImporterValidate()(prevent uninstalling theconfigmodule itself)\Drupal\content_moderation\EventSubscriber\ConfigImportSubscriber::onConfigImporterValidate()(similar to the bundle one above)That's literally it! Everything else is just blindly accepted by the configuration system.
None of the config import UIs nor any of those event subscribers are calling
SchemaCheckTrait::checkConfigSchema(). And none of them are callingvalidate()on the config objects.If we did do thorough validation
\Drupal\Tests\config\Functional\ConfigImportUITestwould have failed since [##3361534].Comment #43
wim leers#38:
The logic you refer to was written in #2245729: Add missing configuration schema in Color component, in May 2014. It was specifically written to help us find missing config schemas. Config schemas were not required initially, until they turned out to be essential for i18n support. It was even broader then:
That's also a good reminder that just like I wrote in my previous comment,
SchemaCheckTraitis used ONLY by tests. Its original purpose was to make sure that the config schema for Drupal core is complete & accurate. All we're doing here, is expanding further upon that.Also, it took only a month to realize that this alone was too broad/not precise enough: #1978714: Entity reference doesn't update its field settings when referenced entity bundles are deleted already tightened that logic in June 2014, to:
This issue is just generalizing that very same pattern.
Yes, it is a change. But that was also true back then for
type: sequence.If we don't do this, then it is impossible for modules to assume a value will be set if the config is valid. We'd force every single module to specify additional validation constraints to guarantee the value is not NULL. That would still result in the
NotNullconstraint being used everywhere. The only difference is that you'd make the DX:nullable: true|falsein the config schema)nullable: true|falseon primitive types, it'd be inconsistent with how it works for sequences. If the default would not benullable: false, then it'd again be inconsistent with how it works for sequences.Great point! There is a simple reason I have not done that yet:
\Drupal\Core\Config\Schema\SchemaCheckTrait::checkValue()will become obsolete: we'll be able to eventually delete it. Otherwise we end up with the same logic twice. I'm just keeping::checkValue()unchanged to minimize disruption. Changing it like you suggest would cause more disruption: contrib modules' tests do not get any validation errors (see the trait), but they do run this. So, added a comment instead. Hope that makes sense to you!The first statement is wrong, the second is accurate. So: updated issue summary. 👍
Comment #44
wim leers#39
(Emphasis mine.)
Correct! And this is also one of the most confusing parts about the entire configuration system! There's a very important distinction between "required values" and "required keys". That's why this issue is specifically titled "required values". See #3364108: Configuration schema & required keys for the issue for required keys.
All of the following combos are possible:
Currently, config schema is not able to express these. And without knowing what is actually intended, it is impossible to fully validate config, which means it's impossible to reliably (without unintentionally breaking the site because the code assumes the presence of a key or value) or safely update config (security implications because of the code's assumptions and the user intentionally submitting certain config).
👎 That would change the meaning of
::setRequired(). Its meaning is specifically for values. Look at\Drupal\Core\Field\FieldConfigInterface::setRequired()and friends: it's always meant to be for "this thing exists, but is its value optional or not?".But this is a moot point already, because you wrote later:
#41:
👍
Comment #45
borisson_I think all the answers in #42-#43-#44 answer the questions by @effulgentsia; moving this back to rtbc.
Comment #46
wim leersI see one important reason for this change is missing from the issue summary.
Quoting
\Symfony\Component\Validator\Constraints\ChoiceValidator::validate():\Symfony\Component\Validator\Constraints\RangeValidator::validate(): same thing.\Symfony\Component\Validator\Constraints\LengthValidator::validate(): same thing.… which makes sense: if the value is optional, it is
NULL, and in that case, none of these validation constraints need to run anymore.That means that:
can easily be bypassed … by just specifying
NULL! 😱The only solution today is to specify
NotNullexplicitly inconstraintstoo, as the first one. But that only works as long as a config schema does not extend/reuse a type, because it is not possible to unset constraints from a parent type.Imagine we had hardened
type: machine_namelike this in Drupal core:then nothing would be able to use
type: machine_nameand make it optional. They'd have to duplicate the entire type definition!Comment #47
phenaproximaI don't understand how this hasn't been committed yet, to be honest. The changes make sense, and so does Wim's explanation in #46. This is a major blocker that holds up virtually all other config validation work at this point, which has a significant knock-on effect on recipes, which are adopting config validation.
I, too, feel like @effulgentsia's questions have been addressed, in conjunction with the write-up at #3395099-11: [meta] Allow config types to opt in to config validation, use "FullyValidatable" constraint at root as signal to opt in to "keys/values are required by default". For whatever it's worth, +1 RTBC here.
Comment #48
wim leersMaking this opt-in using the mechanism proposed at
Comment #49
wim leersorigin/11.xsurfaced 3 new test failures, all in new test coverage added since this was RTBC'd::setRequired()call inTypedConfigManager::buildDataDefinition()then made all existing tests pass, proving there's no more disruption: 0 failures.FullyValidatableconstraint introduces a test failure. The goal is to make this test pass without breaking any other. If that works, I've proven that my proposal to @effulgentsia at #3395099-19: [meta] Allow config types to opt in to config validation, use "FullyValidatable" constraint at root as signal to opt in to "keys/values are required by default" works.::setRequired()call, but this time running it only for those types that have opted in, should make the tests pass again.A change record is needed to make core/contrib/custom maintainers aware of the availability of this mechanism. I first need to update #3364108: Configuration schema & required keys to use the same mechanism.
A follow-up is needed to refine #3402168: Follow-up for #3361534: Config validation errors can still occur for contrib modules, disrupting contrib, to allow contrib modules which have types that are explicitly marked as fully validatable, to run
checkConfigSchema(validate_constraints: true), because today they're forced to usevalidate_constraints: false.Comment #50
phenaproximaI have a few points of non-blocking feedback, but they're non-blocking. :)
Comment #51
wim leersAlthough the MR was now delightfully simple until 1d7f8516, I figured that to get maximum confidence that this will not disrupt anything, we would need explicit test coverage to prove no disruption until opting in (by specifying the
FullyValidatableno-op constraint):SchemaCheckTraitTest)So, added
ConfigEntityValidationTestBase::testRequiredPropertyValuesMissing()for that, which tests to all core config entity types.This unfortunately surfaced a few more small bugs that are probably too out-of-scope to fix here:
Fixes for those are included in this MR, but will hopefully land independently to make this MR simpler again. The choice is to either include those fixes in the MR or to skip the tests of the affected config entity types. But since a whopping 26 config entity types (see the test results) are affected, I figured it was better to include those fixes.
Comment #52
wim leersIssue summary updated to reflect #51.
Comment #53
wim leersChange record created: https://www.drupal.org/node/3404425
Comment #54
phenaproximaI found a few nits but nothing commit-blocking. So, given this issue's importance, I feel okay giving it a deferred RTBC, once the 3 blockers are in.
Comment #55
phenaproxima#3404039: PluginExistsConstraintValidator should return early if given NULL is in.
Comment #56
phenaproximaThis needs a merge/rebase after #3404039: PluginExistsConstraintValidator should return early if given NULL.
Comment #57
wim leers#3404039: PluginExistsConstraintValidator should return early if given NULL just landed. MR updated. One less file being changed 👍 Two blockers to go!
EDIT: conflicted with #56 — I was already doing that obviously :D
Comment #58
phenaproximaHey, look - #3404061: When setting `NotNull` constraint on type: required_label, two validation errors appear landed too!
The final blocker is RTBC, so...
Comment #59
wim leersMerged in upstream and removed the work-around that is now obsolete 👍
Last blocker: #3404023: DefaultSingleLazyPluginCollection::setConfiguration() accepts NULL but ConfigurableInterface::setConfiguration() does not — also RTBC already :) That will allow this MR to remove a mere 10 LoC additions and 2 files touched, but it will mean that this issue's MR is finally matching its intended scope exactly!
Comment #60
effulgentsia commentedThe current MR looks really good to me and addresses all my prior concerns.
I'd like to take an attempt at wordsmithing this a bit more, but not right now. I'll either do that before committing this myself, or if someone else ends up committing it, I'm fine with opening a follow-up for improving code comments.
This issue is tagged "Needs subsystem maintainer review", so would be great to get @alexpott's review if possible. However, if he doesn't have bandwidth for it, I won't block commit of this on that.
Since this MR only adds
FullyValidatableConstraintto menus and shortcut sets, neither of which has dynamic types (plugins or third party settings), I think this is fine for now. However, before addingFullyValidatableConstraintto config roots that include any descendants with dynamic types, I think we need to work out how to handle those boundaries. It's impossible for a config root to know that all of the plugins within it are fully validatable, so I think the semantics ofFullyValidatableConstraintmust be limited to only its descendants with static types. For an element that has an ancestor with a dynamic type, we need to use that ancestor's presence or absence ofFullyValidatableConstraintinstead of checking->getRoot(). But again, we can punt that to a follow-up since that's not surfaced for menus and shortcuts.Comment #61
wim leers@effulgentsia in #60: I'm very glad to read that!
💯 Very good point!
I wrote about this in detail months ago but cannot find it right now. Created #3408165: Fully validatable config schema types: support dynamic types.
Comment #62
wim leersComment #63
phenaproxima#3404023: DefaultSingleLazyPluginCollection::setConfiguration() accepts NULL but ConfigurableInterface::setConfiguration() does not is in, so this is unblocked pending subsystem maintainer review.
Comment #64
wim leersMerged in #3404023: DefaultSingleLazyPluginCollection::setConfiguration() accepts NULL but ConfigurableInterface::setConfiguration() does not 👍 2 fewer files touched by this MR now, and AFAICT zero out-of-scope changes left — all 3 issues listed in #51 have landed 🚢
Comment #65
phenaproximaI've reviewed this a bunch of times and although it's complicated code in very deep and dark places, I think I understand it and it's as clear as it can be.
I think this is ready to go; @alexpott is the subsystem maintainer, so he can check that box if he reviews it for commit. We've already put this on his "desk".
Comment #66
alexpottI've added a comment to the MR that needs thinking about.
Comment #67
wim leersBack to RTBC — the remark was on the one hunk in the MR that was necessary because apparently not a single test in core is doing
$config_entity->set($property_with_a_plugin_collection, NULL). It's a tiny (but important!) aspect of the MR, and I just addressed @alexpott's concern. No material changes to the overall MR.Comment #68
alexpottCommitted 5aa9704 and pushed to 11.x. Thanks!
Comment #70
wim leers🚀
Rebased/updated:
Comment #72
claudiu.cristeaIs this #3416934: Config Inspector crashes on 10.1.x + 10.2.x for `type: mapping` with `nullable: true` due to core bug related?
Comment #73
2dareis2do commentedLooks like this has not been cherry picked for 10.2?
https://github.com/drupal/drupal/blame/11.x/core/lib/Drupal/Core/Config/...
https://github.com/drupal/drupal/blame/10.2.x/core/lib/Drupal/Core/Confi...
Comment #74
2dareis2do commentedComment #75
2dareis2do commentedok patch/diff array_null_config_fatal_errort_fix_10_2.diff fixes the problem in 10.2.
No longer getting fatal error at /admin/reports/config-inspector/search_api_solr.solr_cache.cache_document_default_7_0_0/list
@alexpott can you cherry pick this (or even better change the default target branch to the current release for bug fixes)?
Thank you.
Comment #76
2dareis2do commentedComment #77
2dareis2do commentedComment #78
wim leersI tracked down the root cause of what @2dareis2do ran into: #3416934-23: Config Inspector crashes on 10.1.x + 10.2.x for `type: mapping` with `nullable: true` due to core bug. No contrib disruption happens unless you were specifically using the Config Inspector module 👍