|
1 | 1 | import { type z } from "zod"; |
2 | 2 | import type { PrismaClient } from "@trigger.dev/database"; |
3 | | -import { boundedIn, prisma, type PrismaClientOrTransaction } from "~/db.server"; |
| 3 | +import { $transaction, boundedIn, prisma, type PrismaClientOrTransaction } from "~/db.server"; |
4 | 4 | import { |
5 | 5 | FEATURE_FLAG, |
6 | 6 | type FeatureFlagCatalogSchema, |
@@ -240,28 +240,36 @@ export async function replaceGlobalFeatureFlags( |
240 | 240 | } |
241 | 241 | ): Promise<void> { |
242 | 242 | const canDeleteLocked = params.unlockLockedFlags && !params.isManagedCloud; |
243 | | - const upsertOps: ReturnType<typeof client.featureFlag.upsert>[] = []; |
| 243 | + const toUpsert: { key: FeatureFlagKey; value: unknown }[] = []; |
244 | 244 | const keysToDelete: string[] = []; |
245 | 245 |
|
246 | 246 | for (const key of params.catalogKeys) { |
247 | 247 | if (key in params.requestedFlags) { |
248 | | - const value = params.requestedFlags[key]; |
249 | | - upsertOps.push( |
250 | | - client.featureFlag.upsert({ |
251 | | - where: { key }, |
252 | | - create: { key, value: value as any }, |
253 | | - update: { value: value as any }, |
254 | | - }) |
255 | | - ); |
| 248 | + toUpsert.push({ key, value: params.requestedFlags[key] }); |
256 | 249 | } else if (canDeleteLocked || !GLOBAL_LOCKED_FLAGS.includes(key)) { |
257 | 250 | keysToDelete.push(key); |
258 | 251 | } |
259 | 252 | } |
260 | 253 |
|
261 | | - await client.$transaction([ |
262 | | - ...upsertOps, |
263 | | - ...(keysToDelete.length > 0 |
264 | | - ? [client.featureFlag.deleteMany({ where: { key: { in: boundedIn(keysToDelete) } } })] |
265 | | - : []), |
266 | | - ]); |
| 254 | + const applied = await $transaction(client, "replaceGlobalFeatureFlags", async (tx) => { |
| 255 | + for (const { key, value } of toUpsert) { |
| 256 | + await tx.featureFlag.upsert({ |
| 257 | + where: { key }, |
| 258 | + create: { key, value: value as any }, |
| 259 | + update: { value: value as any }, |
| 260 | + }); |
| 261 | + } |
| 262 | + |
| 263 | + if (keysToDelete.length > 0) { |
| 264 | + await tx.featureFlag.deleteMany({ where: { key: { in: boundedIn(keysToDelete) } } }); |
| 265 | + } |
| 266 | + |
| 267 | + return true; |
| 268 | + }); |
| 269 | + |
| 270 | + // The helper resolves undefined instead of throwing when Prisma errors are swallowed. This |
| 271 | + // write deletes flags, so treat a transaction that did not run as a failure the caller sees. |
| 272 | + if (!applied) { |
| 273 | + throw new Error("replaceGlobalFeatureFlags: transaction did not complete"); |
| 274 | + } |
267 | 275 | } |
0 commit comments