Skip to content

Commit a7b6cab

Browse files
Merge branch 'feat/business-rules-evaluate' into feat/business-rules-run
Brings in the rule-name control-character fix from #1912. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 parents 1c2abde + e29c916 commit a7b6cab

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

‎packages/uipath-platform/src/uipath/platform/business_rules/_business_rules_service.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
session, the undeployed rule from the Studio project being debugged.
55
"""
66

7+
import unicodedata
78
from collections.abc import Mapping
89
from typing import Any, Dict, List, Optional, Tuple
910

@@ -373,7 +374,9 @@ def _validate_rule_name(rule_name: str, field: str) -> None:
373374
for forbidden in ("/", "\\", "..", "%"):
374375
if forbidden in rule_name:
375376
raise ValueError(f"{field} must not contain '{forbidden}'")
376-
if any(not ch.isprintable() for ch in rule_name):
377+
# Control characters only (Unicode category Cc), as the .NET client checks;
378+
# a non-breaking or zero-width space is allowed in a name.
379+
if any(unicodedata.category(ch) == "Cc" for ch in rule_name):
377380
raise ValueError(f"{field} must not contain control characters")
378381

379382

‎packages/uipath-platform/tests/services/test_business_rules_service.py‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,46 @@ def _one_decision(**outputs: Any) -> dict[str, Any]:
8484
class TestRunContext:
8585
@pytest.mark.parametrize(
8686
"rule_name",
87-
["", " ", "a/b", "a\\b", "a..b", "a%20b", "a\nb", "x" * 257],
87+
[
88+
"",
89+
" ",
90+
"a/b",
91+
"a\\b",
92+
"a..b",
93+
"a%20b",
94+
"a\nb",
95+
"a\x00b",
96+
"a\x7fb",
97+
"x" * 257,
98+
],
8899
)
89100
def test_rejects_unsafe_rule_names(
90101
self, service: BusinessRulesService, rule_name: str
91102
) -> None:
92103
with pytest.raises(ValueError, match="name"):
93104
service.run(rule_name, {}, folder_key=FOLDER_KEY)
94105

106+
@pytest.mark.parametrize(
107+
"rule_name",
108+
["Loan\u00a0Pricing", "Loan\u200bPricing", "Préstamo Tarifa", "贷款定价"],
109+
)
110+
def test_accepts_names_the_dotnet_client_accepts(
111+
self,
112+
httpx_mock: HTTPXMock,
113+
service: BusinessRulesService,
114+
evaluate_url: str,
115+
rule_name: str,
116+
) -> None:
117+
# Only control characters are refused, as in the .NET client: a
118+
# non-breaking space pasted into a name is not one.
119+
httpx_mock.add_response(url=evaluate_url, json=_response())
120+
121+
service.run(rule_name, {}, folder_key=FOLDER_KEY)
122+
123+
request = httpx_mock.get_request()
124+
assert request is not None
125+
assert json.loads(request.content)["businessRuleName"] == rule_name
126+
95127
@pytest.mark.parametrize("value", [["age", 14], "age=14", 14])
96128
def test_rejects_non_mapping_input(
97129
self, httpx_mock: HTTPXMock, service: BusinessRulesService, value: Any

0 commit comments

Comments
 (0)