Merge pull request #2793 from garden-co/fix/load-as-upsertUnique

Explicit loadAs in CoList.upsertUnique to use it without loaded context
This commit is contained in:
Matteo Manchi
2025-08-21 18:02:00 +02:00
committed by GitHub
5 changed files with 89 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
"jazz-tools": patch
---
Explicit loadAs in CoList.upsertUnique to use it without loaded context

View File

@@ -621,7 +621,11 @@ export class CoList<out Item = any> extends Array<Item> implements CoValue {
resolve?: RefsToResolveStrict<L, R>;
},
): Promise<Resolved<L, R> | null> {
let listId = CoList._findUnique(options.unique, options.owner.id);
const listId = CoList._findUnique(
options.unique,
options.owner.id,
options.owner._loadedAs,
);
let list: Resolved<L, R> | null = await loadCoValueWithoutMe(this, listId, {
...options,
loadAs: options.owner._loadedAs,

View File

@@ -159,6 +159,8 @@ export function setActiveAccount(account: Account) {
*
* Takes care of restoring the active account after the callback is run.
*
* If the callback returns a promise, waits for it before restoring the active account.
*
* @param callback - The callback to run.
* @returns The result of the callback.
*/
@@ -168,6 +170,14 @@ export function runWithoutActiveAccount<Result>(
const me = Account.getMe();
activeAccountContext.set(null);
const result = callback();
if (result instanceof Promise) {
return result.finally(() => {
activeAccountContext.set(me);
return result;
}) as Result;
}
activeAccountContext.set(me);
return result;
}

View File

@@ -3,10 +3,15 @@ import { assert, beforeEach, describe, expect, test, vi } from "vitest";
import { Account, Group, subscribeToCoValue, z } from "../index.js";
import {
Loaded,
activeAccountContext,
co,
coValueClassFromCoValueClassOrSchema,
} from "../internal.js";
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
import {
createJazzTestAccount,
runWithoutActiveAccount,
setupJazzTestSync,
} from "../testing.js";
import { waitFor } from "./utils.js";
const Crypto = await WasmCrypto.create();
@@ -906,6 +911,29 @@ describe("CoList unique methods", () => {
expect(result?.[2]).toBe("item3");
});
test("upsertUnique without an active account", async () => {
const account = activeAccountContext.get();
const ItemList = co.list(z.string());
const sourceData = ["item1", "item2", "item3"];
const result = await runWithoutActiveAccount(() => {
return ItemList.upsertUnique({
value: sourceData,
unique: "new-list",
owner: account,
});
});
expect(result).not.toBeNull();
expect(result?.length).toBe(3);
expect(result?.[0]).toBe("item1");
expect(result?.[1]).toBe("item2");
expect(result?.[2]).toBe("item3");
expect(result?._owner).toEqual(account);
});
test("upsertUnique updates existing list", async () => {
const ItemList = co.list(z.string());
const group = Group.create();

View File

@@ -1548,6 +1548,46 @@ describe("Creating and finding unique CoMaps", async () => {
});
});
test("upserting without an active account", async () => {
const account = activeAccountContext.get();
// Schema
const Event = co.map({
title: z.string(),
identifier: z.string(),
external_id: z.string(),
});
// Data
const sourceData = {
title: "Test Event Title",
identifier: "test-event-identifier",
_id: "test-event-external-id",
};
const activeEvent = await runWithoutActiveAccount(() => {
return Event.upsertUnique({
value: {
title: sourceData.title,
identifier: sourceData.identifier,
external_id: sourceData._id,
},
unique: sourceData.identifier,
owner: account,
});
});
expect(activeEvent).toEqual({
title: sourceData.title,
identifier: sourceData.identifier,
external_id: sourceData._id,
});
assert(activeEvent);
expect(activeEvent._owner).toEqual(account);
});
test("upserting an existing value", async () => {
// Schema
const Event = co.map({