feat: add multi-join support to Data Fabric entity queries#543
feat: add multi-join support to Data Fabric entity queries#543aayushuipath wants to merge 4 commits into
Conversation
Port the `joins` query capability from the Python SDK (UiPath/uipath-python#1616) to queryRecordsById. Adds an EntityJoin type and a `joins` query option that lets a structured query pull fields from related entities by matching a field on the base entity to a field on a related entity; supplying several composes a multi-entity (multi-join) query. joins are sent in the POST body and added to excludeFromPrefix so they are not OData $-prefixed, matching the existing aggregates/groupBy handling. joinType reuses the existing JoinType enum for consistency with SourceJoinCriteria. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review findingsTwo inline comments posted + one file missing from the diff: Inline comments
Not in diff —
|
Mirror the Python SDK's documented constraint (max 3 joins, all of the same JoinType) in the EntityJoin and joins option JSDoc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review findingsOne new inline comment posted this run: tests/unit/services/data-fabric/entities.test.ts line 1712 — No integration test covers the new |
Address Claude review on PR #543: - Fix the queryRecordsById @example: move JoinType into the block's top import so the multi-join snippet is copy-pasteable (no mid-block import). - Add a guarded integration test for the joins parameter on queryRecordsById, driven by DATA_FABRIC_TEST_JOIN_* fixture env vars; skipped when unset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
✅ No issues found. Checked for bugs and CLAUDE.md compliance. |
Address Claude review on PR #543: - Fix the queryRecordsById @example: move JoinType into the block's top import so the multi-join snippet is copy-pasteable (no mid-block import). - Add a guarded integration test for the joins parameter on queryRecordsById, driven by DATA_FABRIC_TEST_JOIN_* fixture env vars; skipped when unset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
33e8ffd to
0479a6e
Compare
Review findingsOne new inline comment posted this run: entities.ts line 568 — The @param options was updated in the service class but src/models/data-fabric/entities.models.ts was not updated. That file is the TypeDoc source for the public docs site; without the update, the published docs will show no mention of joins. Three spots need fixing: EntityServiceModel.queryRecordsById @param and @example, and EntityMethods.queryRecords @param and @example (add JoinType to the import and add a multi-join example). |
Address Claude re-review on PR #543: the service-class JSDoc had `joins` but the canonical `entities.models.ts` interface (what TypeDoc renders) did not. Add `joins` to the @param and a multi-join @example (with JoinType in the import) for both EntityServiceModel.queryRecordsById and EntityMethods.queryRecords. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| process.env.DATA_FABRIC_TEST_JOIN_RELATED_FIELD_NAME, | ||
| ); | ||
|
|
||
| it.skipIf(!hasJoinFixture)('should query records with a single cross-entity join', async () => { |
There was a problem hiding this comment.
Per convention: "Always throw new Error() when test preconditions are not met — whether it's missing config or missing test data. Never use … silent skips — silent skips hide unrunnable tests and make CI green when tests aren't actually exercised." describe.skip and it.skipIf fall in the same bucket — CI stays green without the test running.
The fix matches the pattern already in this test for missing entityId: remove the hasJoinFixture const + surrounding comment, change it.skipIf(!hasJoinFixture) back to a plain it, and add a throw inside the test body right after the entityId check:
| it.skipIf(!hasJoinFixture)('should query records with a single cross-entity join', async () => { | |
| it('should query records with a single cross-entity join', async () => { |
Then inside the test body, after the entityId guard:
if (
!config.dataFabricTestJoinEntityName ||
!config.dataFabricTestJoinFieldName ||
!config.dataFabricTestJoinRelatedEntityName ||
!config.dataFabricTestJoinRelatedFieldName
) {
throw new Error(
'DATA_FABRIC_TEST_JOIN_* env vars are required for the join integration test',
);
}This makes the test fail (not skip) when the fixture is absent, so a CI run that omits the join env vars is immediately visible as a failure rather than a quiet skip.
There was a problem hiding this comment.
Keeping the skip here intentionally. A join needs a second related entity, and the DATA_FABRIC_TEST_JOIN_* fixtures aren't provisioned in the CI tenant, so switching to a throw would fail test-and-build on every run that doesn't set them. This also matches the existing pattern in this file, where tests that can't run under the CI token use describe.skip (the schema-write scope blocks). The test runs and asserts the join round-trip once the fixture vars are set locally or in a tenant that has them. Resolving as won't-fix.
There was a problem hiding this comment.
integration tests run in a pre-defined org+tenant (procodeapps org/integrationtest tenant). If there is anything missing in this org+tenant, can we create that (missing second entity), and remove this skip? @aayushuipath
There was a problem hiding this comment.
we should ideally not skip tests in case of missing test data/required artifacts
Review findingsOne new inline comment posted this run: tests/integration/shared/data-fabric/entities.integration.test.ts line 975 — |
|
| */ | ||
| export interface EntityJoin { | ||
| /** Name of the base (left) entity that owns `joinFieldName`. */ | ||
| entityName?: string; |
There was a problem hiding this comment.
how is this optional? can join exist without entityName? Do same consideration for other fields as well to check if they are truly optional in join context
| process.env.DATA_FABRIC_TEST_JOIN_RELATED_FIELD_NAME, | ||
| ); | ||
|
|
||
| it.skipIf(!hasJoinFixture)('should query records with a single cross-entity join', async () => { |
There was a problem hiding this comment.
integration tests run in a pre-defined org+tenant (procodeapps org/integrationtest tenant). If there is anything missing in this org+tenant, can we create that (missing second entity), and remove this skip? @aayushuipath
| process.env.DATA_FABRIC_TEST_JOIN_RELATED_FIELD_NAME, | ||
| ); | ||
|
|
||
| it.skipIf(!hasJoinFixture)('should query records with a single cross-entity join', async () => { |
There was a problem hiding this comment.
we should ideally not skip tests in case of missing test data/required artifacts
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { JoinType } from '@uipath/uipath-typescript/entities'; |
There was a problem hiding this comment.
since we are using existing JoinType, update its JSDoc



Summary
Adds multi-join support to Data Fabric entity queries in the TypeScript SDK, porting the
joinscapability from the Python SDK (UiPath/uipath-python#1616).queryRecordsByIdalready supportedfilterGroup,selectedFields,sortOptions,expansionLevel,aggregates,groupBy, and pagination — but not cross-entity joins. This PR closes that gap so TS reaches parity with Python's structuredquery.What changed
EntityJoin(src/models/data-fabric/entities.types.ts) mirroring the PythonEntityJoinmodel:entityName,joinType,joinFieldName,relatedEntityName,relatedFieldNamejoinTypereuses the existingJoinTypeenum (LeftJoin) for consistency withSourceJoinCriteria, rather than a loose string.joins?: EntityJoin[]option onEntityQueryRecordsOptions. Supply one entry per related entity; several entries compose a multi-entity query.queryRecordsByIdwiring (src/services/data-fabric/entities.ts):joinsflows into the POST body and is added toexcludeFromPrefixso it is not OData$-prefixed — identical handling toaggregates/groupBy. No endpoint change: the existing…/EntityService/entity/{id}/query(V1) endpoint already acceptsjoins.tests/unit/services/data-fabric/entities.test.ts): join pass-through,excludeFromPrefixinclusion, andjoinTypeserializing to the enum string value.Wire format
POST datafabric_/api/EntityService/entity/{id}/query { "joins": [ { "entityName": "Order", "joinType": "LeftJoin", "joinFieldName": "customerId", "relatedEntityName": "Customer", "relatedFieldName": "Id" } ] }Usage
Scope note
The Python PR #1616 was a broad entities-service expansion. This PR ports only the multi-join feature that was requested. The related V2
binningsclause (the only thing in Python that routes to thev2/.../queryendpoint) is intentionally left out and can follow separately if needed.Backward compatibility
Additive only — one new optional type and one new optional query option. No existing signatures or behavior change.
Test plan
npm run typecheck— cleannpm run lint(oxlint) — 0 warnings, 0 errorsnpx vitest run— 1882 unit tests pass (incl. 3 new join tests)npm run build— succeeds;EntityJoinpresent indist/entities/index.d.ts🤖 Generated with Claude Code