🐛 handle idempotency when adding/removing labels from sites/resources

This commit is contained in:
Fred KISSIE
2026-05-11 16:57:53 +02:00
parent 2fd519e102
commit b9ab35a05b
2 changed files with 44 additions and 31 deletions

View File

@@ -173,7 +173,9 @@ export const labels = pgTable("labels", {
.notNull() .notNull()
}); });
export const siteLabels = pgTable("siteLabels", { export const siteLabels = pgTable(
"siteLabels",
{
siteLabelId: serial("siteLabelId").primaryKey(), siteLabelId: serial("siteLabelId").primaryKey(),
siteId: integer("siteId") siteId: integer("siteId")
.references(() => sites.siteId, { .references(() => sites.siteId, {
@@ -185,9 +187,13 @@ export const siteLabels = pgTable("siteLabels", {
onDelete: "cascade" onDelete: "cascade"
}) })
.notNull() .notNull()
}); },
(t) => [unique("site_label_uniq").on(t.siteId, t.labelId)]
);
export const resourceLabels = pgTable("resourceLabels", { export const resourceLabels = pgTable(
"resourceLabels",
{
resourceLabelId: serial("resourceLabelId").primaryKey(), resourceLabelId: serial("resourceLabelId").primaryKey(),
resourceId: integer("resourceId") resourceId: integer("resourceId")
.references(() => resources.resourceId, { .references(() => resources.resourceId, {
@@ -199,7 +205,9 @@ export const resourceLabels = pgTable("resourceLabels", {
onDelete: "cascade" onDelete: "cascade"
}) })
.notNull() .notNull()
}); },
(t) => [unique("resource_label_uniq").on(t.resourceId, t.labelId)]
);
export const targets = pgTable("targets", { export const targets = pgTable("targets", {
targetId: serial("targetId").primaryKey(), targetId: serial("targetId").primaryKey(),

View File

@@ -106,13 +106,14 @@ export async function attachLabelToItem(
); );
} }
// idempotent, calling this endpoint multiple times should attach the label only once
await db await db
.insert(siteLabels) .insert(siteLabels)
.values({ .values({
labelId, labelId,
siteId siteId
}) })
.returning(); .onConflictDoNothing();
} }
if (resourceId) { if (resourceId) {
@@ -133,10 +134,14 @@ export async function attachLabelToItem(
); );
} }
await db.insert(resourceLabels).values({ // idempotent, calling this endpoint multiple times should attach the label only once
await db
.insert(resourceLabels)
.values({
labelId, labelId,
resourceId resourceId
}); })
.onConflictDoNothing();
} }
return response(res, { return response(res, {