[REST] API /file-format/parse: do not extend thing/channel config#5295
Conversation
2d87743 to
97b8d85
Compare
There was a problem hiding this comment.
Pull request overview
Adjusts YAML and DSL Thing parsing so isolated models (used by /file-format/parse) do not get their Thing/Channel configurations implicitly extended with default-valued parameters.
Changes:
- Propagates “isolated model” context through Thing creation/merge to preserve only user-provided config.
- Skips applying default configuration from config descriptions for channels when parsing isolated models.
- Uses defensive
Configurationcopies when callingThingHandlerFactory.createThing(...)to avoid side effects.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/things/YamlThingProvider.java | Adds isolated-model-aware Thing/channel creation and merge behavior (and adjusts handler-factory refresh logic accordingly). |
| bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/internal/GenericThingProvider.xtend | Adds isolated-model-aware merge/config handling and prevents applying default channel config for isolated models. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| thing.getChannels().forEach(channel -> { | ||
| for (String name : channel.getConfiguration().keySet()) { | ||
| Object value = channel.getConfiguration().get(name); | ||
| logger.info("initial channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | ||
| value.getClass().getSimpleName()); | ||
| } | ||
| }); |
There was a problem hiding this comment.
The added INFO logs for channel configuration values have the same issues: they may expose secrets and value.getClass() will NPE if a channel config contains a null value. Please remove or make them null-safe and ensure sensitive values are not logged.
| logger.info("before merge thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | ||
| } | ||
| thingFromHandler.getChannels().forEach(channel -> { | ||
| for (String name : channel.getConfiguration().keySet()) { | ||
| Object value = channel.getConfiguration().get(name); | ||
| logger.info("before merge channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | ||
| value.getClass().getSimpleName()); | ||
| } | ||
| }); | ||
| mergeThing(thingFromHandler, thing, isolatedModel); | ||
| for (String name : thingFromHandler.getConfiguration().keySet()) { | ||
| Object value = thingFromHandler.getConfiguration().get(name); | ||
| logger.info("after merge thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | ||
| } | ||
| thingFromHandler.getChannels().forEach(channel -> { | ||
| for (String name : channel.getConfiguration().keySet()) { | ||
| Object value = channel.getConfiguration().get(name); | ||
| logger.info("after merge channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | ||
| value.getClass().getSimpleName()); |
There was a problem hiding this comment.
The additional INFO logging around the merge (before merge... / after merge...) prints all configuration entries and types. This is high-volume and risks leaking sensitive config into logs. Also, the same null-value NPE risk exists with value.getClass(). Please remove these logs or make them trace-level and redact values.
| logger.info("before merge thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | |
| } | |
| thingFromHandler.getChannels().forEach(channel -> { | |
| for (String name : channel.getConfiguration().keySet()) { | |
| Object value = channel.getConfiguration().get(name); | |
| logger.info("before merge channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | |
| value.getClass().getSimpleName()); | |
| } | |
| }); | |
| mergeThing(thingFromHandler, thing, isolatedModel); | |
| for (String name : thingFromHandler.getConfiguration().keySet()) { | |
| Object value = thingFromHandler.getConfiguration().get(name); | |
| logger.info("after merge thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | |
| } | |
| thingFromHandler.getChannels().forEach(channel -> { | |
| for (String name : channel.getConfiguration().keySet()) { | |
| Object value = channel.getConfiguration().get(name); | |
| logger.info("after merge channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | |
| value.getClass().getSimpleName()); | |
| if (logger.isTraceEnabled()) { | |
| String valueType = value != null ? value.getClass().getSimpleName() : "null"; | |
| logger.trace("before merge thing config: {} (type={})", name, valueType); | |
| } | |
| } | |
| thingFromHandler.getChannels().forEach(channel -> { | |
| for (String name : channel.getConfiguration().keySet()) { | |
| Object value = channel.getConfiguration().get(name); | |
| if (logger.isTraceEnabled()) { | |
| String valueType = value != null ? value.getClass().getSimpleName() : "null"; | |
| logger.trace("before merge channel config {}: {} (type={})", | |
| channel.getUID().getId(), name, valueType); | |
| } | |
| } | |
| }); | |
| mergeThing(thingFromHandler, thing, isolatedModel); | |
| for (String name : thingFromHandler.getConfiguration().keySet()) { | |
| Object value = thingFromHandler.getConfiguration().get(name); | |
| if (logger.isTraceEnabled()) { | |
| String valueType = value != null ? value.getClass().getSimpleName() : "null"; | |
| logger.trace("after merge thing config: {} (type={})", name, valueType); | |
| } | |
| } | |
| thingFromHandler.getChannels().forEach(channel -> { | |
| for (String name : channel.getConfiguration().keySet()) { | |
| Object value = channel.getConfiguration().get(name); | |
| if (logger.isTraceEnabled()) { | |
| String valueType = value != null ? value.getClass().getSimpleName() : "null"; | |
| logger.trace("after merge channel config {}: {} (type={})", | |
| channel.getUID().getId(), name, valueType); | |
| } |
| for (String name : thingFromHandler.getConfiguration().keySet()) { | ||
| Object value = thingFromHandler.getConfiguration().get(name); | ||
| logger.info("after merge thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | ||
| } | ||
| thingFromHandler.getChannels().forEach(channel -> { | ||
| for (String name : channel.getConfiguration().keySet()) { | ||
| Object value = channel.getConfiguration().get(name); | ||
| logger.info("after merge channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | ||
| value.getClass().getSimpleName()); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Same concern for the after merge INFO logs: they expose full configuration content (potential secrets), add significant log noise, and will NPE when a config value is null. Please remove them or convert to trace-level with redaction and null-safe type handling.
| for (String name : thing.getConfiguration().keySet()) { | ||
| Object value = thing.getConfiguration().get(name); | ||
| logger.info("initial thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | ||
| } | ||
| thing.getChannels().forEach(channel -> { | ||
| for (String name : channel.getConfiguration().keySet()) { | ||
| Object value = channel.getConfiguration().get(name); | ||
| logger.info("initial channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | ||
| value.getClass().getSimpleName()); | ||
| } | ||
| }); |
There was a problem hiding this comment.
The new INFO-level logging of thing configuration values can leak sensitive data (e.g., passwords/tokens) into logs, and value.getClass() will throw NPE when a config value is null (Configuration allows null values). Please remove these logs or downgrade to TRACE/DEBUG with null-safe formatting and without printing raw config values.
| for (String name : thing.getConfiguration().keySet()) { | |
| Object value = thing.getConfiguration().get(name); | |
| logger.info("initial thing: {} => {} ({})", name, value, value.getClass().getSimpleName()); | |
| } | |
| thing.getChannels().forEach(channel -> { | |
| for (String name : channel.getConfiguration().keySet()) { | |
| Object value = channel.getConfiguration().get(name); | |
| logger.info("initial channel {}: {} => {} ({})", channel.getUID().getId(), name, value, | |
| value.getClass().getSimpleName()); | |
| } | |
| }); | |
| if (logger.isDebugEnabled()) { | |
| for (String name : thing.getConfiguration().keySet()) { | |
| Object value = thing.getConfiguration().get(name); | |
| String valueType = (value != null) ? value.getClass().getSimpleName() : "null"; | |
| logger.debug("Initial thing configuration parameter '{}' of thing '{}' has type {}", name, | |
| thing.getUID(), valueType); | |
| } | |
| thing.getChannels().forEach(channel -> { | |
| for (String name : channel.getConfiguration().keySet()) { | |
| Object value = channel.getConfiguration().get(name); | |
| String valueType = (value != null) ? value.getClass().getSimpleName() : "null"; | |
| logger.debug("Initial channel configuration parameter '{}' of channel '{}' has type {}", name, | |
| channel.getUID(), valueType); | |
| } | |
| }); | |
| } |
|
This is a draft! I plan of course to remove all the new INFO logs. They are here just for my tests. |
The /file-format/parse API does no more extend the thing and channel configuration with parameters having a default value. The output will contain hte thing/channel configuration parameters provided in the input. It is fixed for YAML and DSL formats. Related to openhab#5238 Signed-off-by: Laurent Garnier <lg.hc@free.fr>
97b8d85 to
0d5db33
Compare
|
@wborn : please restart a copilot review, the PR is now fully tested and ready for a review. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def dispatch void merge(Configuration target, Configuration source, boolean keepSourceConfig) { | ||
| if (keepSourceConfig) { | ||
| target.setProperties(Map.of()) | ||
| } | ||
| source.keySet.forEach [ |
There was a problem hiding this comment.
Please add/extend unit tests around the isolated-model parsing flow to cover the new keepSourceConfig merge behavior (clearing the target config before copying source keys). This should verify that /file-format/parse for DSL does not materialize default-valued config parameters that were omitted in the input.
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
|
I will try to add unit tests at least for YAML. |
Done for usual models populating the thing registry and for isolated models used by hte /file-format/parse REST API. These tests are also a way to test the YamlThingOrovider. Signed-off-by: Laurent Garnier <lg.hc@free.fr>
e82d71e to
d92110b
Compare
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
d92110b to
7b06bbe
Compare
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
|
Note that build failure in integration tests is not linked to my PR. |
@holgerfriedrich : can you please retrigger a build, it looks like the build is working for few other PRs. The integration test is probably unstable. |
|
@lolodomo yes, the integration tests are running again and I have retriggered the build, but no luck yet. You should be able to re-trigger (all builds) as well by adding the label "rebuild". Or is setting labels restricted to core-maintainers? |
Not possible to change a label if you are not maintainer. |
That has something to do with the configuration of the repo. In the edit: Maybe it depends on some group membership that gives access to labels in |
|
@openhab/core-maintainers : please review that PR. It is confirmed to be an important fix for our current discussion about editing in Main UI code tab. |
|
I haven't looked at the code, but I did test this PR and it solved the issue, i.e. the defaults are no longer being generated, which is what we want. |
holgerfriedrich
left a comment
There was a problem hiding this comment.
LGTM, let's give it a try!
|
@holgerfriedrich : we need a backporting to the 5.1 branch. I hope it will work without any problem. |
|
@lolodomo I will have a look later today. @kaikreuzer maybe we should shift our 5.1 patch release to the weekend after this has been properly done. |
|
@lolodomo It took a bit more effort to backport and get the tests working again: Do you see an issue backporting #5250 and #5282 as well? I had the adapt 2 lines in the tests. I did not want to backport #5280, as it changes the data type of the returned value from BigDecimal to String. I have not yet pushed this changes to 5.1.x. |
I am not at all in favor of backporting #5250 as it is a significant change only tested by one person and I am even not sure it will not be refactored in final 5.2 as @jimtng found a simpler approach. I believe we need a new PR on branch 5.1.x (even without any unit tests if this is what adds "conflicts"). I can try to create that PR this evening. |
) * [REST] API /file-format/parse: do not extend thing/channel config The /file-format/parse API does no more extend the thing and channel configuration with parameters having a default value. The output will contain hte thing/channel configuration parameters provided in the input. It is fixed for YAML and DSL formats. Unit tests added for YAML models containing Thing Signed-off-by: Laurent Garnier <lg.hc@free.fr>
|
@lolodomo I did not use 5250. I have now pushed my proposal to a new branch on origin (could not get it to my personal repo, as I use a separate setup for the backports): https://github.com/openhab/openhab-core/tree/backports/5.1.x/5295 If you confirm, I can push this to 5.1.x. |
Sorry, I don't understand what you have done. I still see code from #5250 in your new branch. |
) * [REST] API /file-format/parse: do not extend thing/channel config The /file-format/parse API does no more extend the thing and channel configuration with parameters having a default value. The output will contain hte thing/channel configuration parameters provided in the input. It is fixed for YAML and DSL formats. Unit tests added for YAML models containing Thing Signed-off-by: Laurent Garnier <lg.hc@free.fr>
|
@lolodomo obviously I screwed it up. |
) * [REST] API /file-format/parse: do not extend thing/channel config The /file-format/parse API does no more extend the thing and channel configuration with parameters having a default value. The output will contain hte thing/channel configuration parameters provided in the input. It is fixed for YAML and DSL formats. Unit tests added for YAML models containing Thing Signed-off-by: Laurent Garnier <lg.hc@free.fr>
|
Backported to 5.1.x |
The /file-format/parse API does no more extend the thing and channel configuration with parameters having a default value. The output will contain hte thing/channel configuration parameters provided in the input.
It is fixed for YAML and DSL formats.
Related to #5238
Unit tests added for YAML models containing Thing
Done for usual models populating the thing registry and for isolated models used by hte /file-format/parse REST API.
These tests are also a way to test the YamlThingProvider.