Skip to content

Add optional rule UID to DSL Rule file syntax#5449

Open
lolodomo wants to merge 19 commits intoopenhab:mainfrom
lolodomo:dsl_rules_uid
Open

Add optional rule UID to DSL Rule file syntax#5449
lolodomo wants to merge 19 commits intoopenhab:mainfrom
lolodomo:dsl_rules_uid

Conversation

@lolodomo
Copy link
Copy Markdown
Contributor

@lolodomo lolodomo commented Mar 27, 2026

Closes #5437

Allow avoiding issue described in #5415 by setting an UID.

Also avoid notifying the rule registry for isolated models.

Also Refactor change notification.

@lolodomo lolodomo requested a review from a team as a code owner March 27, 2026 11:02
@lolodomo lolodomo marked this pull request as draft March 27, 2026 11:02
@lolodomo lolodomo marked this pull request as ready for review March 27, 2026 13:37
@lolodomo
Copy link
Copy Markdown
Contributor Author

Withis rule file:

rule "Initialize demo items"
when
  System started
then
  DemoLocation.postUpdate("52,9")
  DemoContact.postUpdate(OPEN)
  DemoString.postUpdate("Hello SmartHome!")
  DemoDateTime.postUpdate(new DateTimeType())
  DemoNumber.postUpdate(12.34)
end

rule "Test time of day trigger" myTestRule [ Test, "my test" ]
when
  Time is 13:26
then
  logInfo("Rule","Test")
end

The result in Main UI:
image

image

Closes openhab#5437

Allow avoiding iussue described in openhab#5415 by setting an UID.

Also avoid notifying the rule registry for isolated models.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
@lolodomo
Copy link
Copy Markdown
Contributor Author

Going into draft mode until I check what happens if several same UID are used.

@lolodomo lolodomo marked this pull request as draft March 28, 2026 09:11
@lolodomo lolodomo changed the title [DSL rule] Add optional rule UID to DSL file syntax [DSL rule] Add optional rule UID to DSL rule file syntax Mar 28, 2026
@lolodomo lolodomo changed the title [DSL rule] Add optional rule UID to DSL rule file syntax [DSL rule] Add optional rule UID to DSL Rule file syntax Mar 28, 2026
@openhab-bot
Copy link
Copy Markdown
Collaborator

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/rules-and-rule-templates-yaml-integration/168568/143

@lolodomo lolodomo changed the title [DSL rule] Add optional rule UID to DSL Rule file syntax Add optional rule UID to DSL Rule file syntax Mar 29, 2026
@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Mar 29, 2026

Going into draft mode until I check what happens if several same UID are used.

No problem, it is detected by the registry (as for other stuff - items, things, ...).

I realized that the syntax did not accept hyphen inside the UID and not a number at the beginning of the ID, I will have to extend that and implement the same control on the UID as what @Nadahar implemented in the YAML provider (":" also accepted).

@florian-h05 or @jsjames : can you please tell me what is the syntax accepted in Main UI for the rule UID ? We need the same to allow "conversions".

@florian-h05
Copy link
Copy Markdown
Contributor

@lolodomo
Copy link
Copy Markdown
Contributor Author

So Main UI accepts leading hypen and does not accept colon

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Mar 29, 2026

So Main UI accepts leading hypen and does not accept colon

It might be that it's time to formalize the format for the UID. I think it's probably "a bit random" what has been done until now, when I made "my choice", I did so by looking around and trying to "sum it up". It probably shouldn't be very restricted though, to avoid breaking existing rules.

@lolodomo
Copy link
Copy Markdown
Contributor Author

For compatibility reason, I propose to at least cover Main UI pattern.

@florian-h05
Copy link
Copy Markdown
Contributor

It might be that it's time to formalize the format for the UID.

I support that.
If we only apply the new format for new rules, and don’t validate existing UIDs against it, it shouldn’t be a problem to be more strict in Main UI.

@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Mar 30, 2026

I plan to create a new RuleUID extending AbstractUID.
Edit: or simply add a method isValidRuleUID.
The allowed syntax will be exactly the one already used by the YAML provider.
This will cause problem only for existing UI rules with UID starting with a hyphen, conversion to DSL or YAML will then fail. We could then remove the leading hyphens as a workaround. But it should not concern a lot of users as it is not natural to do that I believe.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Mar 30, 2026

But it should not concern a lot of users as it is not natural to do that I believe.

Unless you're a C programmer 😝

@florian-h05
Copy link
Copy Markdown
Contributor

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Mar 30, 2026

BTW, also see https://github.com/openhab/openhab-core/blob/4d9cacc3ae14bef89409949a2235bd1a9c414e1e/bundles/org.openhab.core/src/main/java/org/openhab/core/util/UIDUtils.java and what it does.

I'm not sure what the benefit is of such encoding. I mean, as far as I know, "any string" will work here (as that's basically what we have today). We have URL encoding to apply if used as a URL parameter, JSON has its way to encode this.

I think we should ideally only restrict what can cause problems somewhere. I'm not sure what that is, but some of the "symbols" can probably collide with something somewhere..?

@lolodomo
Copy link
Copy Markdown
Contributor Author

I propose to be consistent with other UID like thing UID.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Mar 30, 2026

I propose to be consistent with other UID like thing UID.

Isn't that much more restrictive than it is now, and will break a lot of existing rules. Is that really "worth it"?

@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Apr 4, 2026

I can't quite understand under which circumstances it will actually occur.

If your file has only comments for example.
I will fix that.

lolodomo added 2 commits April 6, 2026 09:27
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Apr 6, 2026

I added a last change, inspired from what @mherwege did for sitemaps. Before notifying the removal of a rule with a certain UID, first check if it exists with this UID in another model file and then rather notify a modification.

@dilyanpalauzov
Copy link
Copy Markdown
Contributor

Irrespective of DSL, what is supposed to happen, if a rule is created, which has the same UID, as a different, pre-existing rule?

Is there a test for DSL Rules, verifying the above behaviour, when two distinct .rules files create two rules with the same UID?

@rkoshak
Copy link
Copy Markdown

rkoshak commented Apr 7, 2026

Irrespective of DSL, what is supposed to happen, if a rule is created, which has the same UID, as a different, pre-existing rule?

In JS and I imagine the other languages as well I think which ever rule is created second will overwrite the first one.

In the UI, I think MainUI will prevent you from creating the rule if you try to give it the same UID as an existing rule.

@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Apr 7, 2026

Irrespective of DSL, what is supposed to happen, if a rule is created, which has the same UID, as a different, pre-existing rule?

The (rule) registry is checking unicity of id, it will accept the first and rejects the next ones with the same id, a warning is logged. It is true if rules are coming from the same provider or different providers.

It is the same principle as for items or things, in fact a general principle for any of our registries

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 11, 2026

@lolodomo As I said, it wasn't important to move the variable declaration back 😉

What is more important is to get this merged, so that I can continue with the file conversion. What exactly is this hanging on?

@lolodomo
Copy link
Copy Markdown
Contributor Author

What is more important is to get this merged, so that I can continue with the file conversion. What exactly is this hanging on?

Probably someone available that can take few minutes to review the PR.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 11, 2026

The Java 25 build failed, but I'm not convinced that it actually has anything to do with this PR:

Error:  Error   : Exit code remote process 1: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/25.0.2-10/x64/bin/java -cp /home/runner/work/openhab-core/openhab-core/itests/org.openhab.core.voice.tests/target/test/tmp/testing/itest/cnf/cache/7.2.1/bnd-cache/biz.aQute.launcher/biz.aQute.launcher.pre.jar -Dlauncher.properties=/home/runner/work/openhab-core/openhab-core/itests/org.openhab.core.voice.tests/generated/launch13933713361707104413.properties -Djdk.util.zip.disableZip64ExtraFieldValidation=true -Dorg.osgi.service.http.port=38575 -DJETTY_AVAILABLE_PROCESSORS=4 --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED --add-opens=java.naming/javax.naming.spi=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED -javaagent:/home/runner/.m2/repository/org/mockito/mockito-core/5.21.0/mockito-core-5.21.0.jar -Xshare:off -ea -Dlauncher.runpath=/home/runner/work/openhab-core/openhab-core/itests/org.openhab.core.voice.tests/target/test/tmp/testing/itest/cnf/cache/7.2.1/org.openhab.core.bom.runtime-index/org.eclipse.osgi-3.18.0.v20220516-2155.jar,/home/runner/work/openhab-core/openhab-core/itests/org.openhab.core.voice.tests/target/test/tmp/testing/itest/cnf/cache/7.2.1/bnd-cache/biz.aQute.launcher/biz.aQute.launcher-7.2.1.jar aQute.launcher.pre.EmbeddedLauncher

@mherwege
Copy link
Copy Markdown
Contributor

It feels a little unnatural to have the (required) name in the syntax followed by an optional uid, without indication of what is what (except for the documentation).
What if you would make the first field the uid and the second field the name. The name could default to the uid if none is provided. Would that have an adverse impact somewhere? I understand it leads to problems if the same name has been given to different rules, but would that be expected?

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 11, 2026

It feels a little unnatural to have the (required) name in the syntax followed by an optional uid, without indication of what is what (except for the documentation).
What if you would make the first field the uid and the second field the name. The name could default to the uid if none is provided. Would that have an adverse impact somewhere? I understand it leads to problems if the same name has been given to different rules, but would that be expected

I think this is how it will have to be because of backwards compatibility. UIDs will still be autogenerated if not specified, and the name will still be the name, so that all existing rules will be interpreted the same way as they are today. If you turn this around, all existing rules users have (and that they refer to from other scripts/rules) will get new UIDs and mayhem will ensue. That would make this a huge breaking change instead of a relatively minor enhancement.

@mherwege
Copy link
Copy Markdown
Contributor

mherwege commented Apr 11, 2026

It feels a little unnatural to have the (required) name in the syntax followed by an optional uid, without indication of what is what (except for the documentation).
What if you would make the first field the uid and the second field the name. The name could default to the uid if none is provided. Would that have an adverse impact somewhere? I understand it leads to problems if the same name has been given to different rules, but would that be expected

I think this is how it will have to be because of backwards compatibility. UIDs will still be autogenerated if not specified, and the name will still be the name, so that all existing rules will be interpreted the same way as they are today. If you turn this around, all existing rules users have (and that they refer to from other scripts/rules) will get new UIDs and mayhem will ensue. That would make this a huge breaking change instead of a relatively minor enhancement.

I understand that. But does anyone call file based DSL rules from other rules? Is it even possible in an easy way? You are working on supporting this in another PR. Are there any other places we rely on the current exact UID (except for the enable/disable bug)? If this is unlikely, now is the time to adapt. If it is common pattern, we should go with the guaranteed backward compatible change, at the cost of not entirely solving the bug, because it can still happen for the auto-generated UIDs.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 11, 2026

I understand that. But does anyone call file based DSL rules from other rules? Is it even possible in an easy way? You are working on supporting this in another PR. Are there any other places we rely on the current exact UID (except for the enable/disable bug)?

I don't think it's a problem to call DSL rules from non-DSL rules, so you could do that from e.g. a JS rule. What doesn't work is running other rules from a DSL rule.

But, I'm not only worried about the references, but about UID collisions. There is nothing that prevents multiple rules from having the same name, and this would suddenly result in an error:

bilde

ediit: I also expect that many may prefer to continue using the autogenerated UIDs, after all it mostly doesn't matter what the UID is. But, making it possible to specify the UID is still very helpful if you for some reason want to specify it, if you for example don't like it to change if you modify the .rules file so that you add another rule before it (today the UIDs are indexed). Not to mention that it actually makes it possible to convert rules between formats, which you can't really do with the autogenerated IDs.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 316 to +320
String name = rule.getName();
String uid = modelName + "-" + index;
String uid = rule.getUid();
if (uid == null || uid.isBlank()) {
uid = modelName + "-" + index;
}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User-provided rule UIDs from the DSL are accepted verbatim (only blank/null falls back to auto-generated). Because rule UIDs are embedded directly into event topics (e.g., openhab/rules/{ruleID}/...), a UID containing '/' will produce an invalid topic that RuleEventFactory cannot parse (it expects exactly 4 topic segments). Add validation/sanitization for configured UIDs (at minimum reject '/' and fall back to the auto-generated UID, ideally with a warning log) to prevent runtime event failures.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a separate PR, #5467, that deals with this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User-provided rule UIDs from the DSL are accepted verbatim (only blank/null falls back to auto-generated). Because rule UIDs are embedded directly into event topics (e.g., openhab/rules/{ruleID}/...), a UID containing '/' will produce an invalid topic that RuleEventFactory cannot parse (it expects exactly 4 topic segments). Add validation/sanitization for configured UIDs (at minimum reject '/' and fall back to the auto-generated UID, ideally with a warning log) to prevent runtime event failures.

I think this comment from copilot is the best reason to reject my wild idea. It does correctly point out the UID should be validated more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nadahar : maybe it is good to exclude the / in that PR, even if there is a more general discussion in parallel in another PR?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lolodomo I have already modified #5467 so that both / and \ are excluded in all "suggested patterns". I just wished we could agree on something and move that PR forward.

@dilyanpalauzov
Copy link
Copy Markdown
Contributor

But does anyone call file based DSL rules from other rules? Is it even possible in an easy way?

With RuleManager.runNow() a rule can be executed from another rule. There is also HTTP endpoint for the same. It does not matter in which language the callee or the caller are written.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>

Rule:
'rule' name=(STRING|ID) ('uid=' uid=(STRING|ID))? ('[' tags+=(ID|STRING) (',' tags+=(ID|STRING))* ']')?
'rule' name=(STRING|ID) ('uid' '='? uid=(STRING|ID))? ('[' tags+=(ID|STRING) (',' tags+=(ID|STRING))* ']')?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make the = sign optional? I see potential for parser issues when anything is ever changed to the syntax (add a field).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No equal sign - neither mandatory, nor optional - is also a viable approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No equal sign - neither mandatory, nor optional - is also a viable approach.

I think that would be inconsistent with what has been done in other DSL formats.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After rule, after when and after then there is no equal sign. So why should after uid be an =?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring to the other formats, e.g.

type=ID '=' configuration=STRING
('['
properties+=ModelProperty? (',' properties+=ModelProperty)*
']')?
;
ModelProperty:
key=ID '=' value+=ValueType (',' value+=ValueType)*

I also think it's "safer" to have a more explicit syntax, it allows more flexibility for potential future changes to the format.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not far from forgiving and closing that PR...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion, make = required to make this explicit and be done with it. I am fine with all else, so when I find the time we can proceed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DSL rule] Add support for rule UID when using DSL file format

8 participants