codeconsole commented on code in PR #16732:
URL: https://github.com/apache/grails-core/pull/16732#discussion_r4417798614
##########
grails-doc/src/en/guide/upgrading/upgrading90x.adoc:
##########
@@ -1241,3 +1241,36 @@ Grails no longer registers a character encoding filter
of its own. It used to, a
* `spring.servlet.encoding.enabled: false` turns the encoding filter off.
* The CAS plugin's single sign-out filter, which reads a parameter of every
POST, is registered right after the encoding filter.
* A filter of your own that reads request parameters needs a later order than
`Ordered.HIGHEST_PRECEDENCE`, or form input reaches controllers decoded as
ISO-8859-1. One registered at exactly `Ordered.HIGHEST_PRECEDENCE` through a
`FilterRegistrationBean` runs before the encoding filter.
+
+==== 21. A Configuration Key Below a Value That Is Not a Map Is Absent
+
+In Grails 8, `getProperty` on `grailsApplication.config` answered a dotted key
that runs past a value that is not a map with that value: `server.url.port`
when `server.url` is a string, `server.ports.0` when `server.ports` is a list,
or `grails.gorm.default.constraints.nullable` when
`grails.gorm.default.constraints` is a closure from `application.groovy`.
`containsProperty` of such a key was `true`. In Grails 9 the key is absent:
`getProperty` returns `null` or the default, `containsProperty` returns
`false`, and `navigate` answers `null`.
+
+Below a list, a whole-number segment names an element instead, as Spring Boot
reads it, and so does an index after the name of the list, whether the list
comes from `application.yml` or from `application.groovy`:
Review Comment:
Added in dc9924d334, in §21 and in the `grails-9-upgrade` skill. A
placeholder in a list element is not resolved by `config`, by index or in the
whole list, as in Grails 8. The `Environment` resolves it, so the text says to
read such an element there or to bind the list with `@ConfigurationProperties`.
I reproduced it with Grails' YAML loader at the new head. `app.origins.1`,
`app.origins[1]` and the elements of `getProperty('app.origins', List)` all
come back as `${EXAMPLE_ORIGIN:…}`. So does a value in a map inside a list,
`app.rows.0.url`. The `Environment` answers `https://default.example.com` for
each. Spring Boot's YAML loader is not affected, because
`PropertySourcesConfig` resolves each element of a list it reads from a source
that is not a `NavigableMapPropertySource`.
The hook fix goes in a separate PR against 7.0.x. The hook and the loader
there are the same as on 9.0.x.
##########
grails-core/src/main/groovy/org/grails/config/NavigableMapConfig.java:
##########
@@ -380,20 +380,61 @@ private Object
getValueWithDotNotatedKeySupport(NavigableMap configMap, String k
}
if (end > start) {
String segment = key.substring(start, end);
- if (first) {
- value = configMap.get(segment);
- first = false;
- } else if (value instanceof Map) {
- value = ((Map<?, ?>) value).get(segment);
- } else {
- break;
+ value = childOf(first ? configMap : value, segment);
+ first = false;
+ if (value == null) {
+ return null;
}
}
start = end + 1;
}
return value;
}
+ /**
+ * The child a segment of a key names: the entry of a map, or the element
of a list at a whole-number segment, as
+ * Spring Boot reads {@code server.ports.0}. A segment with an index after
a name, {@code ports[0]}, names the element
+ * of the list under {@code ports} when the map holds no key of that name
itself. Anything else below a value that
+ * is not a map, such as a child of a scalar, does not exist.
+ */
+ private static Object childOf(Object value, String segment) {
+ if (value instanceof Map) {
+ Map<?, ?> map = (Map<?, ?>) value;
+ if (map.containsKey(segment)) {
+ return map.get(segment);
+ }
+ int bracket = segment.indexOf('[');
+ if (bracket > 0 && segment.endsWith("]")) {
+ Object child = map.get(segment.substring(0, bracket));
+ for (String index : segment.substring(bracket + 1,
segment.length() - 1).split("\\]\\[", -1)) {
+ child = childOf(child, index);
+ if (child == null) {
+ return null;
+ }
+ }
+ return child;
+ }
+ return null;
+ }
+ if (value instanceof List<?> list) {
+ int index = listIndex(segment);
+ return index >= 0 && index < list.size() ? list.get(index) : null;
+ }
+ return null;
+ }
+
+ private static int listIndex(String segment) {
Review Comment:
Done in 60414c1f79. A whole-number segment indexes a list only when it is
`0` or does not start with `0`. That applies in `getProperty` and in
`navigate`, so `server.ports.01` and `server.ports[01]` are absent.
- `PropertySourcesConfigSpec` adds the case to the comparison with a
`StandardEnvironment` with `ConfigurationPropertySources` attached, which
answers `null` too.
- `NavigableMapSpec` covers `navigate`.
- The guide and the skill now list a leading zero among the segments that
stay absent.
- The new assertions failed at e1c1d0bc1c. `grails-bootstrap` (308) and
`grails-core` (1087) pass.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]