Skip to content

feat(entities): relationships_as_scalar query option for FK joins#1818

Merged
milind-jain-uipath merged 3 commits into
mainfrom
feat/entities-relationships-as-scalar
Jul 21, 2026
Merged

feat(entities): relationships_as_scalar query option for FK joins#1818
milind-jain-uipath merged 3 commits into
mainfrom
feat/entities-relationships-as-scalar

Conversation

@milind-jain-uipath

@milind-jain-uipath milind-jain-uipath commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the source header param on the Data Fabric query API with an explicit relationships_as_scalar: bool option on EntitiesService.query_entity_records[_async]. When True, the request sends queryOptions.relationshipsAsScalar in the v1/query/execute body so FQS types relationship (FK) fields as their scalar id — letting a query join on relationshipField = Other.Id (FQS contract, UiPath/CommonEntityPlatform#5611). Optional and backward-compatible: default False omits queryOptions and preserves prior behaviour.

How

  • _query_entity_records_spec adds body["queryOptions"] = {"relationshipsAsScalar": True} only when the flag is set; the removed source path (and its x-uipath-source header + HEADER_SOURCE import) is gone.
  • The bool threads through query_entity_records[_async]_query_entities_for_records[_async] → the shared spec builder, sync and async identically.
  • uipath-platform 0.2.10 → 0.2.11; both lockfiles regenerated.

Security

  • The new option is a boolean — no SQL string interpolation; sql_query validation is unchanged.
  • Cross-tenant isolation is unaffected: routingContext (folder-scoped routing derived from folders_map) is untouched; the flag only changes FK column typing, not tenant/folder scoping. Removing the header weakens nothing — BaseService.request still injects auth/trace/routing headers.
  • No mass-assignment surface: a single known boolean; the server ignores unknown properties.

Tests

  • Replaced the two source-header tests with body-assertion tests: sync + async queryOptions.relationshipsAsScalar == True when set, and queryOptions omitted by default.
  • Affected suite green (test_entities_service.py, 153 passed). Changed-code coverage: the edited query methods are 100% covered (whole-file 94.7% / 99.4%). ruff + ruff format + mypy clean.

Review triage

Independent adversarial review returned P0/P1 count: 0.

  • P3 (fixed): async new-body behaviour had no direct assertion — added test_query_entity_records_async_sets_relationships_as_scalar_option.
  • P3 (deferred): body = ...get("json") or call_args[1].get("json") fallback is dead — left as-is to match the existing idiom in the sibling routing tests; cosmetic, no behaviour impact.

🤖 Generated with Claude Code

Manual verification (local end-to-end)

Screenshot 2026-07-16 at 12 23 31 AM Screenshot 2026-07-16 at 12 23 22 AM

Verified through a low-code agent run (uipath-robot) against a local QueryEngine build of UiPath/CommonEntityPlatform#5611 (QE isn't live on alpha yet), over folder-scoped Data Fabric entities SalesOrderSpace(OrderNumber, Amount, account → AccountSpace) and AccountSpace(Id, Name). The SDK sent queryOptions.relationshipsAsScalar: true on v1/query/execute; QE returned 200 in both cases.

  • Scalar projectionSELECT OrderNumber, Amount, account FROM SalesOrderSpace: the account relationship field returns as a scalar id (GUID string), null when unset — not VARIANT/expanded object.
  • FK joinSELECT s.OrderNumber, s.Amount, a.Id, a.Name FROM SalesOrderSpace s LEFT JOIN AccountSpace a ON a.Id = s.account: validates and executes (two per-entity fragments federated → SQLite join), correct pairing (SO-3001→Acme Corp, SO-3002→Globex, SO-3003→null via LEFT JOIN). Without the scalar option this join is rejected as <VARIANT> = <VARCHAR>.

Copilot AI review requested due to automatic review settings July 15, 2026 16:05
@github-actions github-actions Bot added test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-integrations labels Jul 15, 2026

Copilot AI left a comment

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.

Pull request overview

This PR updates the Data Fabric “federated SQL query” API surface in EntitiesService to use an explicit relationships_as_scalar: bool option (sent as queryOptions.relationshipsAsScalar in the request body) instead of the previous x-uipath-source header approach, enabling FK relationship fields to be treated as scalar IDs for join scenarios.

Changes:

  • Replace the source parameter/header with a relationships_as_scalar boolean that conditionally adds queryOptions.relationshipsAsScalar to the query/execute request body.
  • Thread the option through both sync and async query paths and update docstrings accordingly.
  • Update tests to assert the new request-body behavior and bump uipath-platform to 0.2.11 (lockfiles regenerated).

Reviewed changes

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

Show a summary per file
File Description
packages/uipath/uv.lock Bumps the editable uipath-platform version to 0.2.11.
packages/uipath-platform/uv.lock Bumps the uipath-platform package version to 0.2.11.
packages/uipath-platform/tests/services/test_entities_service.py Replaces source-header assertions with request-body queryOptions.relationshipsAsScalar assertions (sync + async).
packages/uipath-platform/src/uipath/platform/entities/_entity_data_service.py Implements conditional queryOptions.relationshipsAsScalar emission and removes the source header plumbing.
packages/uipath-platform/src/uipath/platform/entities/_entities_service.py Exposes relationships_as_scalar on the public API and updates docstrings.
packages/uipath-platform/pyproject.toml Version bump to 0.2.11.

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

Comment on lines 1823 to 1825
def query_entity_records(
self, sql_query: str, source: Optional[str] = None
self, sql_query: str, relationships_as_scalar: bool = False
) -> List[Dict[str, Any]]:

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.

Fixed in 0cbad36 — made relationships_as_scalar keyword-only (*) on both query_entity_records and query_entity_records_async, so an old positional source call now raises TypeError instead of silently coercing the truthy string. Added test_query_entity_records_flag_is_keyword_only to guard it.

Comment on lines 1855 to 1857
async def query_entity_records_async(
self, sql_query: str, source: Optional[str] = None
self, sql_query: str, relationships_as_scalar: bool = False
) -> List[Dict[str, Any]]:

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.

Fixed in 0cbad36 — made relationships_as_scalar keyword-only (*) on both query_entity_records and query_entity_records_async, so an old positional source call now raises TypeError instead of silently coercing the truthy string. Added test_query_entity_records_flag_is_keyword_only to guard it.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80f208188b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@traced(name="entity_query_records", run_type="uipath")
def query_entity_records(
self, sql_query: str, source: Optional[str] = None
self, sql_query: str, relationships_as_scalar: bool = False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the new flag keyword-only

With this public signature, an existing caller using the previously supported positional source argument, e.g. query_entity_records(sql, "LOW_CODE_AGENT"), is still accepted because non-empty strings are truthy; it now sends queryOptions.relationshipsAsScalar and drops x-uipath-source instead of failing. Since the commit removes the keyword form too, make the new flag keyword-only (and/or validate it is a bool) for both sync and async wrappers so old positional calls don't silently execute with different semantics.

Useful? React with 👍 / 👎.

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.

Fixed in 0cbad36 — made relationships_as_scalar keyword-only (*) on both query_entity_records and query_entity_records_async, so an old positional source call now raises TypeError instead of silently coercing the truthy string. Added test_query_entity_records_flag_is_keyword_only to guard it.

milind-jain-uipath and others added 3 commits July 21, 2026 12:43
Replaces the query/execute `source` header param with an explicit
`relationships_as_scalar` bool that sets `queryOptions.relationshipsAsScalar`
in the request body (FQS contract, UiPath/CommonEntityPlatform#5611). When set,
relationship (FK) fields are typed as their scalar id so a query can join on
`relationshipField = Other.Id`. Optional and backward-compatible (default False).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prevents a released positional `source` call (`query_entity_records(sql,
"LOW_CODE_AGENT")`) from silently coercing the truthy string into
`relationships_as_scalar=True`; such calls now raise TypeError.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0.2.11 was released to PyPI from main (#1822) without this change, so this
PR ships as 0.2.12.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@milind-jain-uipath
milind-jain-uipath force-pushed the feat/entities-relationships-as-scalar branch from 504bc14 to ae80986 Compare July 21, 2026 07:14
milind-jain-uipath added a commit to UiPath/uipath-langchain-python that referenced this pull request Jul 21, 2026
0.2.11 is published without the relationships_as_scalar option (it still
carries the old source param); the option lands in 0.2.12
(UiPath/uipath-python#1818).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@milind-jain-uipath
milind-jain-uipath merged commit 684009f into main Jul 21, 2026
164 checks passed
@milind-jain-uipath
milind-jain-uipath deleted the feat/entities-relationships-as-scalar branch July 21, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:uipath-integrations test:uipath-langchain Triggers tests in the uipath-langchain-python repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants