The GitHub Actions job "Coverage" on 
grails-core.git/fix/spring7-nested-map-conversion has succeeded.
Run started by GitHub user jamesfredley (triggered by jamesfredley).

Head commit for run:
5c99b411ff95ba5faf4afcfe4e9f5f40509f75d1 / James Fredley 
<[email protected]>
fix(config): bind nested settings maps under Spring 7

Spring Framework 7 no longer converts a configuration Map into a type
annotated @Builder(builderStrategy = SimpleStrategy), so nested settings
failed to bind with ConverterNotFoundException. ConfigurationBuilder now
instantiates the target type and populates it from the Map.

The gap is demonstrable on this branch: with the previous ConfigurationBuilder
and only the new spec applied, six scenarios fail with "Expected exception of
type 'ConfigurationException', but got 'ConverterNotFoundException'". 9.0.x
resolves spring-core 7.0.8 via Spring Boot 4.1.0.

The fallback is deliberately narrow, and every guard below exists because
removing it produced an observable failure:

- It engages only when the cause chain contains ConverterNotFoundException,
  so a converter that deliberately rejects a Map is not bypassed.
- ConfigurationException is never suppressed, so unknown-key and
  malformed-value failures still surface instead of being masked by the
  original conversion exception.
- A failure while resolving the raw value throws rather than silently
  falling back, so configuration whose lookup failed is not quietly accepted.
- The instance inherits from the fallback before overrides are applied, and
  each nested level receives its own fallback child, so overriding one field
  does not discard the rest.
- Values are converted to the target property type, including the
  case-insensitive enum path, so multiTenancy.mode: database still binds.
- Class-typed entries resolve through the thread context class loader, the
  same route the top-level Class handling uses, because the resolver's
  converter resolves against the framework class loader and would leave an
  application class such as hibernate.configClass unbound.
- Types that are themselves a Map keep arbitrary entries. HibernateSettings
  extends LinkedHashMap precisely to carry keys like hibernate.hbm2ddl.auto,
  which strict property-only binding would have rejected.
- Flattened descendant keys are bound once through their parent rather than
  rejected, since the resolver flattens nested configuration; a dotted key
  whose first segment is unknown is still rejected.
- Setters are invoked with an explicit single-element argument array so an
  explicit null clears an inherited value.

ConfigurationBuilderSpec grows from 10 to 22 specs covering each of the above.

Known limitation: a PropertyResolver that exposes only an aggregate map, and
not its entries as dotted properties, can still yield null for a configured
scalar. Grails' own DatastoreUtils.createPropertyResolver flattens and is
unaffected. Binding the raw value unconditionally was rejected as a fix
because it would bypass the type conversion above.

Assisted-by: claude-code:claude-opus-5

Report URL: https://github.com/apache/grails-core/actions/runs/31970380177

With regards,
GitHub Actions via GitBox

Reply via email to