Skip to content

fix(data-fabric): align entity field type emission with UI's narrower FieldType enum#489

Open
AditiGoyalUipath wants to merge 2 commits into
mainfrom
fix/data-fabric-field-type-mapping
Open

fix(data-fabric): align entity field type emission with UI's narrower FieldType enum#489
AditiGoyalUipath wants to merge 2 commits into
mainfrom
fix/data-fabric-field-type-mapping

Conversation

@AditiGoyalUipath

Copy link
Copy Markdown
Contributor

Summary

The Manage Entity UI accepts a narrower set of SQL types than the backend (FE doc §3.1, §3.4). When the SDK emitted INT/BIGINT/FLOAT/REAL/DATETIME2 — or omitted the field-level type discriminator entirely — the UI silently rewrote those fields to NVARCHAR on load. Saving again then failed with "Field type cannot be changed" because SqlType.Name is immutable on update (backend doc §6.4), even when the user made no changes.

Changes

1. Emit type (UI logical type) on every outbound field

  • New apiTypeName column in EntitySchemaFieldTypeMap ("text", "number", "dateTime", "relationship", etc.)
  • buildSchemaFieldPayload() now sets type: mapping.apiTypeName on the wire payload
  • FieldMetaData.type carries it back on read so re-upserts preserve it

2. Collapse types the UI rewrites

  • INTEGER, BIG_INTEGER, FLOAT, DOUBLEDECIMAL (was INT/BIGINT/FLOAT/REAL)
  • DATETIMEDATETIMEOFFSET (was DATETIME2)
  • INTEGER/BIG_INTEGER set decimalPrecision: 0 (new INTEGER_DECIMAL_PRECISION constant) to preserve integer semantics
  • FLOAT/DOUBLE retain the user-overridable precision (default 2)

3. Relax FILE-field create

  • FILE no longer requires referenceEntityId/referenceFieldId — server auto-provisions the EntityAttachment FK (backend doc §5.10)
  • FILE no longer emits isForeignKey/referenceType/referenceEntity/referenceField (those are RELATIONSHIP-only)
  • Validation guard tightened from (isRelationship || isFile) to isRelationship only

Test plan

  • npm run typecheck — clean
  • npm run lint — clean
  • npm run test:unit — passes (unit assertions updated for new SQL types, precision, and type field)
  • Integration tests cover:
    • FILE field create without referenceEntityId/referenceFieldId
    • Full create → record-insert → uploadAttachment round-trip
  • Verify in Manage Entity UI: create an entity with INTEGER/FLOAT/DATETIME fields via SDK, open in UI, save without changes — should succeed (previously failed with "Field type cannot be changed")

🤖 Generated with Claude Code

@AditiGoyalUipath AditiGoyalUipath requested a review from a team June 3, 2026 09:20
sqlTypeName: SqlFieldType;
fieldDisplayType: FieldDisplayType;
/** Value emitted as the field's `type` on the wire. */
apiTypeName: 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.

apiTypeName: string holds a finite, fixed set of API wire values — 'uniqueid', 'text', 'number', 'dateTime', 'date', 'boolean', 'multiline', 'relationship', 'choiceSetSingle', 'choiceSetMultiple', 'autonumber'. Per the SDK convention ("Use enums for fixed value sets — NEVER leave raw strings/numbers"), these should be an enum, e.g.:

// entities.types.ts (public — FieldMetaData.type uses it)
export enum EntityApiFieldType {
  UniqueId         = 'uniqueid',
  Text             = 'text',
  Number           = 'number',
  DateTime         = 'dateTime',
  Date             = 'date',
  Boolean          = 'boolean',
  Multiline        = 'multiline',
  Relationship     = 'relationship',
  ChoiceSetSingle  = 'choiceSetSingle',
  ChoiceSetMultiple = 'choiceSetMultiple',
  AutoNumber       = 'autonumber',
}

Then:

  • EntitySchemaFieldMapping.apiTypeName: EntityApiFieldType
  • FieldSchemaPayload.type?: EntityApiFieldType
  • FieldMetaData.type?: EntityApiFieldType (entities.types.ts:539)
  • EntitySchemaFieldTypeMap values use EntityApiFieldType.Text etc.

Since FieldMetaData is public, the enum lives in entities.types.ts, not internal-types.ts.

@vnaren23 vnaren23 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The Manage Entity UI accepts a narrower set of SQL types than the backend (FE doc §3.1, §3.4). When the SDK emitted INT/BIGINT/FLOAT/REAL/DATETIME2 — or omitted the field-level type discriminator entirely

Is the backend meant to accept such wider set of SQL types? Forcing SDK changes just to confer to lack of support from DF UI seems incorrect. If anything we should add these support into DF UI rather than making SDK changes.

expect(mockApiClient.get).toHaveBeenCalledWith(
DATA_FABRIC_ENDPOINTS.ENTITY.GET_BY_ID(ENTITY_TEST_CONSTANTS.ENTITY_ID),
{},
{ headers: {} },

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.

The getById describe block only tests the no-folderKey path. The PR adds options?: EntityGetByIdOptions (with folderKey) to getById, but there's no test that passes a folderKey and verifies the X-UIPATH-FolderKey header is forwarded.

The same gap exists for:

  • downloadAttachment (line ~1188 — only the no-folderKey case is asserted)
  • deleteAttachment (line ~1301)
  • uploadAttachment (the expansionLevel test was updated to include headers: {}, but no folderKey path)
  • updateById — both the schema-update (inner UPSERT/GET calls) and the metadata-update (patch) paths

deleteById (line 2072) already shows the pattern:

it("should pass folderKey via X-UIPATH-FolderKey header when provided", async () => {
  mockApiClient.get.mockResolvedValue(mockResponse);
  const result = await entityService.getById(
    ENTITY_TEST_CONSTANTS.ENTITY_ID,
    { folderKey: ENTITY_TEST_CONSTANTS.FIELD_ID },
  );
  expect(result).toBeDefined();
  expect(mockApiClient.get).toHaveBeenCalledWith(
    DATA_FABRIC_ENDPOINTS.ENTITY.GET_BY_ID(ENTITY_TEST_CONSTANTS.ENTITY_ID),
    { headers: { "X-UIPATH-FolderKey": ENTITY_TEST_CONSTANTS.FIELD_ID } },
  );
});

Please add the equivalent test for each of the five methods listed above. Per conventions, new API-call code paths need 100% coverage.

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review summary

One new finding this run:

Missing folderKey test coverage (discussion_r3347930764) — The PR adds folderKey support to getById, downloadAttachment, deleteAttachment, uploadAttachment, and updateById, but only create and deleteById have tests verifying the X-UIPATH-FolderKey header is actually forwarded. The remaining five methods only assert the no-folderKey path ({ headers: {} }). Per conventions, new API-call code paths require 100% coverage.


Note: the open thread about converting apiTypeName: string to an enum (PRRT_kwDOOpObr86GsYQC) remains unresolved — the same concern also applies to FieldMetaData.type?: string in entities.types.ts:610 which was introduced in this PR and should use the same enum once it's defined.

displayName?: string;
description?: string;
/** UI logical type (e.g. "text", "number", "dateTime"). Required for Manage Entity round-trip. */
type?: 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.

The JSDoc says "Required for Manage Entity round-trip" but the field is typed as optional (?). buildSchemaFieldPayload() always populates this from EntitySchemaFieldTypeMap, where apiTypeName: string is a required field for every EntityFieldDataType entry — so type will always be present on every outbound FieldSchemaPayload.

Making it required matches the documentation and gives compile-time enforcement that future code paths can't accidentally produce a schema payload without the type discriminator (which is precisely the bug this PR fixes):

Suggested change
type?: string;
/** UI logical type (e.g. "text", "number", "dateTime"). Required for Manage Entity round-trip. */
type: string;

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review summary

One new finding this run:

FieldSchemaPayload.type should be required, not optional — The JSDoc says "Required for Manage Entity round-trip" but the field is typed type?: string. Since buildSchemaFieldPayload() always populates it from EntitySchemaFieldTypeMap (where apiTypeName is required for every entry), making the TypeScript signature match — type: string — gives compile-time enforcement that future code paths cannot accidentally omit the discriminator that this PR exists to fix.

Two prior threads remain open (enum for apiTypeName; missing folderKey header tests) — not duplicated here.

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

AditiGoyalUipath and others added 2 commits June 3, 2026 18:15
… FieldType enum

The Manage Entity UI accepts a smaller set of SQL types than the backend
(FE doc §3.1, §3.4). When the SDK emitted INT/BIGINT/FLOAT/REAL/DATETIME2
or omitted the field-level `type` discriminator, the UI silently rewrote
those fields to NVARCHAR on load. The backend then rejected the next save
with "Field type cannot be changed" (backend doc §6.4 — SqlType.Name is
immutable on update), even when the user made no changes.

Two changes:

- Emit `type` (UI logical type: "text", "number", "dateTime", etc.) on
  every outbound field. Carried forward via FieldMetaData on re-upsert.

- Collapse INT/BIGINT/FLOAT/REAL → DECIMAL and DATETIME2 → DATETIMEOFFSET
  in EntitySchemaFieldTypeMap. EntityFieldDataType.INTEGER/BIG_INTEGER
  set decimalPrecision: 0 to preserve integer semantics; FLOAT/DOUBLE
  retain the user-overridable precision (default 2).

FILE behaviour is unchanged in this PR — it still requires user-supplied
references and forwards them on the wire. The FILE → server-provisioned-
references change ships separately in #491.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ieldSchemaPayload

Two PR review fixes:

- Promote the raw wire-vocabulary strings ('text', 'number', 'dateTime', …)
  to a typed enum EntityApiFieldType. Per SDK convention ("Use enums for
  fixed value sets — NEVER leave raw strings/numbers"), and gives users a
  typo-safe contract on the public FieldMetaData.type field.

- Make FieldSchemaPayload.type required (was optional with a JSDoc claim
  of "Required for Manage Entity round-trip"). buildSchemaFieldPayload
  always populates it from EntitySchemaFieldTypeMap, so the optional
  marker only allowed future code paths to silently omit the discriminator
  that this PR exists to fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@AditiGoyalUipath AditiGoyalUipath force-pushed the fix/data-fabric-field-type-mapping branch from 55a2b68 to 6d0b375 Compare June 3, 2026 12:46
expect(f.sqlType).toEqual({ name: "UNIQUEIDENTIFIER", lengthLimit: 300 });
});

it("should emit isForeignKey, referenceEntity, referenceField — but NOT referenceType — for FILE fields", async () => {

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.

The PR description (point 3) states the FILE validation guard was tightened from (isRelationship || isFile) to isRelationship only, and that FILE no longer emits isForeignKey. But buildSchemaFieldPayload in entities.ts is unchanged — lines 1061 and 1084 still read (isRelationship || isFile), so FILE still requires reference IDs and still emits isForeignKey: true.

This test correctly reflects the actual (unchanged) code. The issue is that the parallel updateById test "should throw ValidationError when FILE field is missing reference IDs" was removed from the updateById section on the assumption the guard had been changed — but it wasn't. The create-section equivalent at line 2046 still covers the same buildSchemaFieldPayload code path, so no functional gap, but the updateById-specific explicit assertion was lost without the corresponding implementation change.

Please either:

  • Restore the removed updateById test (it("should throw ValidationError when FILE field is missing reference IDs", ...)) to match the unchanged service code, or
  • Implement the described relaxation (change (isRelationship || isFile)isRelationship on both lines 1061 and 1084 of entities.ts) and remove the test as planned

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review summary

One new finding this run:

Unimplemented FILE-field relaxation (discussion_r3348609731) — The PR description (point 3) claims the FILE validation guard was tightened from (isRelationship || isFile) to isRelationship only, and that FILE no longer emits isForeignKey. However, buildSchemaFieldPayload in entities.ts lines 1061 and 1084 remain unchanged — isFile is still present in both guards. As a consequence, the updateById test "should throw ValidationError when FILE field is missing reference IDs" was removed on the assumption the behaviour was gone, but it still exists. The create-section equivalent at line 2046 still covers the same code path, so no runtime gap, but the explicit updateById coverage was lost without the matching implementation change.


Three prior threads remain open (not duplicated here):

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.

2 participants