feat: look up local row ids by sync id for the sync plugin (Plugin-Sync#4)

This commit is contained in:
LukeGus committed 2026-10-29 12:49:47 -05:00
1 parent 691896db2d
commit 8d26c716b4
6 files changed
+54 -3

No files matched your search

+6
View File
@@ -38,6 +38,7 @@ import { hosts, sshCredentials } from "../database/db/schema.js";
import {
createResolvers,
deleteStoredRow,
findStoredIds,
listStoredRows,
loadWireRow,
loadWireRows,
@@ -271,6 +272,11 @@ export function createSyncHost(): PluginSyncHost {
ensureCore: () => ensureCoreEntities(),
onChanged: (listener) => onEntitiesChanged(listener),
onLocalWrite: (listener) => onLocalWrite(listener),
idsOf: async (type, userId, syncIds) => {
const entity = getEntity(type);
if (!entity || entity.load) return {};
return findStoredIds(entity, userId, syncIds);
},
},
replica: {
+15
View File
@@ -112,6 +112,21 @@ export async function findStoredRow(
return row ?? null;
}
/** The local ids of the user's own rows, by sync id. Unknown ones are left out. */
export async function findStoredIds(
entity: RegisteredSyncEntity,
userId: string,
syncIds: string[],
): Promise<Record<string, number>> {
const ids: Record<string, number> = {};
if (entity.singleton || !entity.table) return ids;
for (const syncId of new Set(syncIds)) {
const row = await findStoredRow(entity, userId, syncId);
if (row && typeof row.id === "number") ids[syncId] = row.id;
}
return ids;
}
/** Gives an old row with no syncId one, so it can take part. */
async function ensureSyncId(
entity: RegisteredSyncEntity,
+25 -2
View File
@@ -8,6 +8,8 @@ const mocks = vi.hoisted(() => ({
getLink: vi.fn(),
generateJWTToken: vi.fn(),
revokeSession: vi.fn(),
getEntity: vi.fn(),
findStoredIds: vi.fn(),
}));
vi.mock("../../utils/auth-manager.js", () => ({
@@ -28,7 +30,7 @@ vi.mock("../../utils/app-version.js", () => ({
vi.mock("../../notify/core-notify.js", () => ({ sendCoreAlert: vi.fn() }));
vi.mock("../../plugins/index.js", () => ({ getPluginRuntime: vi.fn() }));
vi.mock("../../plugins/sync-registry.js", () => ({
getEntity: vi.fn(),
getEntity: mocks.getEntity,
listEntities: () => [{ type: "hosts", owner: "core", order: 10 }],
onEntitiesChanged: vi.fn(),
}));
@@ -55,7 +57,9 @@ vi.mock("../../utils/permission-manager.js", () => ({
vi.mock("../../sync/entities.js", () => ({
registerCoreSyncEntities: vi.fn(),
}));
vi.mock("../../sync/store.js", () => ({}));
vi.mock("../../sync/store.js", () => ({
findStoredIds: mocks.findStoredIds,
}));
vi.mock("../../sync/wire.js", () => ({}));
vi.mock("../../sync/records.js", () => ({
listChanges: mocks.listChanges,
@@ -215,3 +219,22 @@ describe("host.links", () => {
});
});
});
describe("host.entities.idsOf", () => {
it("maps sync ids to the user's own row ids", async () => {
const entity = { type: "snippets", table: {} };
mocks.getEntity.mockReturnValue(entity);
mocks.findStoredIds.mockResolvedValue({ a: 3 });
expect(
await createSyncHost().entities.idsOf!("snippets", "u", ["a", "b"]),
).toEqual({ a: 3 });
expect(mocks.findStoredIds).toHaveBeenCalledWith(entity, "u", ["a", "b"]);
});
it("answers nothing for an unknown or built type", async () => {
mocks.getEntity.mockReturnValue(undefined);
expect(await createSyncHost().entities.idsOf!("x", "u", ["a"])).toEqual({});
mocks.getEntity.mockReturnValue({ type: "y", load: vi.fn() });
expect(await createSyncHost().entities.idsOf!("y", "u", ["a"])).toEqual({});
});
});