Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-navigate-useentityids.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@proofkit/fmodata": patch
---

Fix navigate() losing per-table useEntityIds after Database.from() mutation fix
17 changes: 14 additions & 3 deletions packages/fmodata/src/client/entity-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
} from "../orm/table";
import { FMTable as FMTableClass, getDefaultSelect, getTableColumns, getTableName, getTableSchema } from "../orm/table";
import type { ExecutionContext } from "../types";
import { resolveTableId } from "./builders/table-utils";
import type { Database } from "./database";
import { DeleteBuilder } from "./delete-builder";
import { InsertBuilder } from "./insert-builder";
Expand Down Expand Up @@ -361,12 +362,22 @@ export class EntitySet<Occ extends FMTable<any, any>, DatabaseIncludeSpecialColu
databaseName: this.databaseName,
context: this.context,
database: this.database,
useEntityIds: this.databaseUseEntityIds,
});
// Resolve navigation names using entity IDs when appropriate
const resolvedRelation = resolveTableId(targetTable, relationName, this.context, this.databaseUseEntityIds);
const resolvedSourceName = resolveTableId(
this.occurrence,
getTableName(this.occurrence),
this.context,
this.databaseUseEntityIds,
);

// Store the navigation info in the EntitySet
// biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern
(entitySet as any).isNavigateFromEntitySet = true;
// biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern
(entitySet as any).navigateRelation = relationName;
(entitySet as any).navigateRelation = resolvedRelation;

// Build the full base path for chained navigations
if (this.isNavigateFromEntitySet && this.navigateBasePath) {
Expand All @@ -382,9 +393,9 @@ export class EntitySet<Occ extends FMTable<any, any>, DatabaseIncludeSpecialColu
// biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern
(entitySet as any).navigateSourceTableName = this.navigateSourceTableName;
} else {
// Initial navigation - source is just the table name
// Initial navigation - source is just the table name (resolved to entity ID if needed)
// biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern
(entitySet as any).navigateSourceTableName = getTableName(this.occurrence);
(entitySet as any).navigateSourceTableName = resolvedSourceName;
}
return entitySet;
}
Expand Down
5 changes: 2 additions & 3 deletions packages/fmodata/src/client/record-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,8 @@ export class RecordBuilder<
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,
});

// Store the navigation info - we'll use it in execute
// Use relation name as-is (entity ID handling is done in QueryBuilder)
const relationId = relationName;
// Store the navigation info - resolve entity ID for relation if needed
const relationId = resolveTableId(targetTable, relationName, this.context, this.databaseUseEntityIds);

// If this RecordBuilder came from a navigated EntitySet, we need to preserve that base path
let sourceTableName: string;
Expand Down
36 changes: 35 additions & 1 deletion packages/fmodata/tests/navigate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@

import { dateField, fmTableOccurrence, textField } from "@proofkit/fmodata";
import { describe, expect, expectTypeOf, it } from "vitest";
import { arbitraryTable, contacts, createMockClient, invoices, lineItems, users } from "./utils/test-setup";
import {
arbitraryTable,
contacts,
contactsTOWithIds,
createMockClient,
invoices,
lineItems,
users,
usersTOWithIds,
} from "./utils/test-setup";

const contactsUsersPathRegex = /^\/contacts\/users/;

Expand Down Expand Up @@ -179,6 +188,31 @@ describe("navigate", () => {
// });
});

it("should preserve useEntityIds from parent EntitySet through navigate", () => {
const db = client.database("test_db");
const query = db.from(contactsTOWithIds).navigate(usersTOWithIds).list();
const qs = query.getQueryString();

// Should use FMTID for table names, not field names
expect(qs).toContain("FMTID:200"); // contacts table entity ID
expect(qs).toContain("FMTID:1065093"); // users table entity ID
expect(qs).not.toContain("/contacts/");
expect(qs).not.toContain("/users?");
});

it("should preserve useEntityIds through record navigate", () => {
const db = client.database("test_db");
const qs = db
.from(contactsTOWithIds)
.get("rec-1")
.navigate(usersTOWithIds)
.select({ name: usersTOWithIds.name })
.getQueryString();

expect(qs).toContain("FMTID:200"); // contacts source
expect(qs).toContain("FMTID:1065093"); // users relation
});

// Issue #107: navigate() doesn't include parent table in URL path
// when defaultSelect is "schema" or an object
describe("with defaultSelect='schema' (#107)", () => {
Expand Down
Loading