fix(server): harden document star idempotency

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-21 13:53:17 +00:00
1 parent 1763f5a76b
commit bcea9310b2
4 files changed
+25 -13

No files matched your search

@@ -397,6 +397,12 @@ describeEmbeddedPostgres("companyArtifactsService", () => {
expect(second.status).toBe(200);
expect(second.body.artifacts.map((artifact: { title: string }) => artifact.title)).toEqual(["User Upload Notes"]);
expect(second.body.artifacts.every((artifact: { source: string }) => artifact.source === "document")).toBe(true);
const incompatibleKind = await request(app).get(
`/api/companies/${companyId}/artifacts?starred=true&kind=image`,
);
expect(incompatibleKind.status).toBe(200);
expect(incompatibleKind.body).toEqual({ artifacts: [], nextCursor: null });
});
it("returns an empty starred view for agent callers", async () => {
@@ -429,12 +429,14 @@ describeEmbeddedPostgres("resource membership routes", () => {
const { companyId, documentId } = await seed();
const app = createApp(db, boardActor(companyId));
const first = await request(app)
.put(`/api/companies/${companyId}/resource-memberships/me/documents/${documentId}`)
.send({ starred: true });
const second = await request(app)
.put(`/api/companies/${companyId}/resource-memberships/me/documents/${documentId}`)
.send({ starred: true });
const [first, second] = await Promise.all([
request(app)
.put(`/api/companies/${companyId}/resource-memberships/me/documents/${documentId}`)
.send({ starred: true }),
request(app)
.put(`/api/companies/${companyId}/resource-memberships/me/documents/${documentId}`)
.send({ starred: true }),
]);
const list = await request(app).get(`/api/companies/${companyId}/resource-memberships/me`);
expect(first.status).toBe(200);
+1 -1
View File
@@ -348,7 +348,7 @@ export function companyArtifactsService(db: Db, storage?: StorageService) {
const artifactSortDates = new Map<string, string>();
const workProductAttachmentIds = new Set<string>();
if (query.starred || query.kind === "all" || query.kind === "document") {
if (query.kind === "all" || query.kind === "document") {
const createdAgent = alias(agents, "document_created_agent");
const updatedAgent = alias(agents, "document_updated_agent");
const documentArtifactId = sql<string>`concat('document:', ${documents.id})`;
+10 -6
View File
@@ -1,4 +1,4 @@
import { and, eq } from "drizzle-orm";
import { and, eq, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import {
agentMemberships,
@@ -491,19 +491,23 @@ export function resourceMembershipService(db: Db, options: ResourceMembershipSer
.onConflictDoUpdate({
target: [documentMemberships.companyId, documentMemberships.userId, documentMemberships.documentId],
set: {
starredAt: now,
updatedAt: now,
starredAt: sql`${documentMemberships.starredAt}`,
updatedAt: sql`${documentMemberships.updatedAt}`,
},
})
.returning();
.returning({
starredAt: documentMemberships.starredAt,
updatedAt: documentMemberships.updatedAt,
inserted: sql<boolean>`xmax = 0`,
});
return {
resourceType: "document",
resourceId: input.documentId,
state: "joined",
starredAt: row?.starredAt ?? now,
updatedAt: row?.updatedAt ?? now,
changed: true,
changeKind: "starred",
changed: row?.inserted === true,
changeKind: row?.inserted === true ? "starred" : null,
policySource: decision.source ?? "oss_default",
};
}