Skip to content

Commit 4ea997f

Browse files
refactor(business-rules): rule_name -> business_rule_name
Internal parameters, locals, _RunTarget's field, the name validator (_validate_business_rule_name) and its constants now say business_rule_name, matching the wire's businessRuleName and BusinessRuleRunResult.business_rule_name. The public run(name, input) and its error messages are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 1fb0b6e commit 4ea997f

2 files changed

Lines changed: 45 additions & 33 deletions

File tree

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

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
_EVALUATE_ENDPOINT = Endpoint("businessrules_/v1/business-rules/evaluate")
3030

3131
_MAX_INPUT_KEYS = 256
32-
_MAX_RULE_NAME_LENGTH = 256
33-
_FORBIDDEN_RULE_NAME_PARTS = ("/", "\\", "..", "%")
32+
_MAX_BUSINESS_RULE_NAME_LENGTH = 256
33+
_FORBIDDEN_BUSINESS_RULE_NAME_PARTS = ("/", "\\", "..", "%")
3434

3535

3636
@dataclass(frozen=True)
@@ -41,7 +41,7 @@ class _RunTarget:
4141
send, or a path still to look up.
4242
"""
4343

44-
rule_name: str
44+
business_rule_name: str
4545
folder_key: Optional[str]
4646
folder_path: Optional[str]
4747

@@ -124,7 +124,7 @@ def run(
124124
"""
125125
run_target = self._prepare_run(name, input, folder_key, folder_path)
126126
request_spec = self._evaluate_spec(
127-
run_target.rule_name,
127+
run_target.business_rule_name,
128128
input,
129129
folder_key=self._resolve_folder_key(run_target),
130130
version=version,
@@ -175,7 +175,7 @@ async def run_async(
175175
"""
176176
run_target = self._prepare_run(name, input, folder_key, folder_path)
177177
request_spec = self._evaluate_spec(
178-
run_target.rule_name,
178+
run_target.business_rule_name,
179179
input,
180180
folder_key=await self._resolve_folder_key_async(run_target),
181181
version=version,
@@ -189,7 +189,7 @@ async def run_async(
189189

190190
def _prepare_run(
191191
self,
192-
rule_name: str,
192+
business_rule_name: str,
193193
rule_input: Dict[str, Any],
194194
folder_key: Optional[str],
195195
folder_path: Optional[str],
@@ -199,13 +199,15 @@ def _prepare_run(
199199
Applies a binding, validates the arguments and picks the folder, so only
200200
the folder lookup and the request itself differ between the two.
201201
"""
202-
rule_name, folder_key, folder_path = self._apply_binding(
203-
rule_name, folder_key, folder_path
202+
business_rule_name, folder_key, folder_path = self._apply_binding(
203+
business_rule_name, folder_key, folder_path
204204
)
205-
_validate_run_arguments(rule_name, rule_input)
205+
_validate_run_arguments(business_rule_name, rule_input)
206206
selected_key, selected_path = self._select_folder(folder_key, folder_path)
207207
return _RunTarget(
208-
rule_name=rule_name, folder_key=selected_key, folder_path=selected_path
208+
business_rule_name=business_rule_name,
209+
folder_key=selected_key,
210+
folder_path=selected_path,
209211
)
210212

211213
@resource_override(resource_type="businessRule")
@@ -217,21 +219,27 @@ def _overridden_resource(
217219
return name, folder_path
218220

219221
def _apply_binding(
220-
self, rule_name: str, folder_key: Optional[str], folder_path: Optional[str]
222+
self,
223+
business_rule_name: str,
224+
folder_key: Optional[str],
225+
folder_path: Optional[str],
221226
) -> Tuple[str, Optional[str], Optional[str]]:
222227
"""Apply a businessRule binding, if one remaps this rule.
223228
224229
The binding names a folder by path. When it applies, that folder
225230
replaces whichever folder the caller gave, including a folder_key, which
226231
the override decorator alone would leave in place next to the new path.
227232
"""
228-
bound_rule_name, bound_folder_path = self._overridden_resource(
229-
rule_name, folder_path=folder_path
233+
bound_business_rule_name, bound_folder_path = self._overridden_resource(
234+
business_rule_name, folder_path=folder_path
235+
)
236+
is_remapped = (bound_business_rule_name, bound_folder_path) != (
237+
business_rule_name,
238+
folder_path,
230239
)
231-
is_remapped = (bound_rule_name, bound_folder_path) != (rule_name, folder_path)
232240
if is_remapped and bound_folder_path:
233241
folder_key = None
234-
return bound_rule_name, folder_key, bound_folder_path
242+
return bound_business_rule_name, folder_key, bound_folder_path
235243

236244
def _select_folder(
237245
self, folder_key: Optional[str], folder_path: Optional[str]
@@ -279,7 +287,7 @@ async def _resolve_folder_key_async(self, run_target: _RunTarget) -> str:
279287

280288
def _evaluate_spec(
281289
self,
282-
rule_name: str,
290+
business_rule_name: str,
283291
rule_input: Dict[str, Any],
284292
*,
285293
folder_key: str,
@@ -290,7 +298,7 @@ def _evaluate_spec(
290298
# The service resolves the rule in this folder and files the run's trace
291299
# and audit record under it.
292300
request_body: Dict[str, Any] = {
293-
"businessRuleName": rule_name,
301+
"businessRuleName": business_rule_name,
294302
"input": rule_input,
295303
}
296304
if _has_value(version):
@@ -377,22 +385,26 @@ def _has_value(text: Optional[str]) -> bool:
377385
return bool(text and text.strip())
378386

379387

380-
def _validate_run_arguments(rule_name: str, rule_input: Dict[str, Any]) -> None:
381-
_validate_rule_name(rule_name)
388+
def _validate_run_arguments(
389+
business_rule_name: str, rule_input: Dict[str, Any]
390+
) -> None:
391+
_validate_business_rule_name(business_rule_name)
382392
_validate_input(rule_input)
383393

384394

385-
def _validate_rule_name(rule_name: str) -> None:
386-
if not _has_value(rule_name):
395+
def _validate_business_rule_name(business_rule_name: str) -> None:
396+
if not _has_value(business_rule_name):
387397
raise ValueError("name must be specified")
388-
if len(rule_name) > _MAX_RULE_NAME_LENGTH:
389-
raise ValueError(f"name must not exceed {_MAX_RULE_NAME_LENGTH} characters")
390-
for forbidden_part in _FORBIDDEN_RULE_NAME_PARTS:
391-
if forbidden_part in rule_name:
398+
if len(business_rule_name) > _MAX_BUSINESS_RULE_NAME_LENGTH:
399+
raise ValueError(
400+
f"name must not exceed {_MAX_BUSINESS_RULE_NAME_LENGTH} characters"
401+
)
402+
for forbidden_part in _FORBIDDEN_BUSINESS_RULE_NAME_PARTS:
403+
if forbidden_part in business_rule_name:
392404
raise ValueError(f"name must not contain '{forbidden_part}'")
393405
# Control characters only (Unicode category Cc), as the .NET client checks;
394406
# a non-breaking or zero-width space is allowed in a name.
395-
if any(unicodedata.category(character) == "Cc" for character in rule_name):
407+
if any(unicodedata.category(character) == "Cc" for character in business_rule_name):
396408
raise ValueError("name must not contain control characters")
397409

398410

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def _single_decision_result(**outputs: Any) -> dict[str, Any]:
8282

8383
class TestRunContext:
8484
@pytest.mark.parametrize(
85-
"rule_name",
85+
"business_rule_name",
8686
[
8787
"",
8888
" ",
@@ -97,31 +97,31 @@ class TestRunContext:
9797
],
9898
)
9999
def test_rejects_unsafe_rule_names(
100-
self, service: BusinessRulesService, rule_name: str
100+
self, service: BusinessRulesService, business_rule_name: str
101101
) -> None:
102102
with pytest.raises(ValueError, match="name"):
103-
service.run(rule_name, {}, folder_key=FOLDER_KEY)
103+
service.run(business_rule_name, {}, folder_key=FOLDER_KEY)
104104

105105
@pytest.mark.parametrize(
106-
"rule_name",
106+
"business_rule_name",
107107
["Loan\u00a0Pricing", "Loan\u200bPricing", "Préstamo Tarifa", "贷款定价"],
108108
)
109109
def test_accepts_names_the_dotnet_client_accepts(
110110
self,
111111
httpx_mock: HTTPXMock,
112112
service: BusinessRulesService,
113113
evaluate_url: str,
114-
rule_name: str,
114+
business_rule_name: str,
115115
) -> None:
116116
# Only control characters are refused, as in the .NET client: a
117117
# non-breaking space pasted into a name is not one.
118118
httpx_mock.add_response(url=evaluate_url, json=_service_response())
119119

120-
service.run(rule_name, {}, folder_key=FOLDER_KEY)
120+
service.run(business_rule_name, {}, folder_key=FOLDER_KEY)
121121

122122
request = httpx_mock.get_request()
123123
assert request is not None
124-
assert json.loads(request.content)["businessRuleName"] == rule_name
124+
assert json.loads(request.content)["businessRuleName"] == business_rule_name
125125

126126
@pytest.mark.parametrize("value", [["age", 14], "age=14", 14])
127127
def test_rejects_non_mapping_input(

0 commit comments

Comments
 (0)