chore: Add Prisma extension to block deleteMany/updateMany with undefined 'in' (#15721)

* chore: Add Prisma extension to block updateMany with undefined 'in'

* Fix type

* types

* Added check for deleteMany as well

* Improved in check

* type

* Updated check to only check undefined and not isArray

* Renamed the guard

* another rename

* Changed undefined check to use typeof

* Types

* Added check

* Update packages/prisma/extensions/disallow-undefined-in-delete-update-many.ts

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>

* Added unit tests

* Added a good case

* Rename

* Rename fix

* Types

* Temporarily disabling the extension for testing

* Added back the extension

* Trying to also log to see if there are swallowed errors

* Update

* removed unused params

* removed console.log

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
This commit is contained in:
Keith Williams
2024-09-13 22:52:06 +09:00
committed by GitHub
co-authored by Hariom Balhara Anik Dhabal Babu
parent 6e7012757f
commit fd8afbc418
3 changed files with 79 additions and 0 deletions
@@ -0,0 +1,38 @@
import { describe, it, expect } from "vitest";
import { checkUndefinedInValue } from "./disallow-undefined-delete-update-many";
describe("Disallow undefined delete/update many", () => {
it("checkUndefinedInValue should throw exception when the 'in' field of where object is undefined", async () => {
const where = {
id: {
in: undefined,
},
};
expect(() => checkUndefinedInValue(where)).toThrowError(
'The "in" value for the field "id" cannot be undefined.'
);
});
it("checkUndefinedInValue should throw exception when a field of where object is undefined", async () => {
const where = {
from: undefined,
};
expect(() => checkUndefinedInValue(where)).toThrowError(
'The value for the field "from" cannot be undefined.'
);
});
it("checkUndefinedInValue should not throw exception when the where object does not contain undefined values", async () => {
const where = {
id: {
in: [1, 2, 3],
},
name: "test name",
};
checkUndefinedInValue(where);
});
});
@@ -0,0 +1,38 @@
import { Prisma } from "@prisma/client";
export const checkUndefinedInValue = (where: any) => {
if (where) {
for (const key in where) {
// INFO: Since this is for $allModels, we don't have a way to get the correct
// where type
const whereInput = where[key as any] as any;
let message;
if (whereInput === undefined) {
message = `The value for the field "${key}" cannot be undefined.`;
throw new Error(message);
}
if (whereInput.hasOwnProperty("in") && typeof whereInput.in === "undefined") {
message = `The "in" value for the field "${key}" cannot be undefined.`;
throw new Error(message);
}
}
}
};
export function disallowUndefinedDeleteUpdateManyExtension() {
return Prisma.defineExtension({
query: {
$allModels: {
async deleteMany({ args, query }) {
checkUndefinedInValue(args.where);
return query(args);
},
async updateMany({ args, query }) {
checkUndefinedInValue(args.where);
return query(args);
},
},
},
});
}
+3
View File
@@ -3,6 +3,7 @@ import { PrismaClient as PrismaClientWithoutExtension } from "@prisma/client";
import { withAccelerate } from "@prisma/extension-accelerate";
import { bookingIdempotencyKeyExtension } from "./extensions/booking-idempotency-key";
import { disallowUndefinedDeleteUpdateManyExtension } from "./extensions/disallow-undefined-delete-update-many";
import { excludePendingPaymentsExtension } from "./extensions/exclude-pending-payment-teams";
import { usageTrackingExtention } from "./extensions/usage-tracking";
import { bookingReferenceMiddleware } from "./middleware";
@@ -25,6 +26,7 @@ export const customPrisma = (options?: Prisma.PrismaClientOptions) =>
.$extends(usageTrackingExtention())
.$extends(excludePendingPaymentsExtension())
.$extends(bookingIdempotencyKeyExtension())
.$extends(disallowUndefinedDeleteUpdateManyExtension())
.$extends(withAccelerate());
// If any changed on middleware server restart is required
@@ -37,6 +39,7 @@ const prismaWithClientExtensions = prismaWithoutClientExtensions
.$extends(usageTrackingExtention())
.$extends(excludePendingPaymentsExtension())
.$extends(bookingIdempotencyKeyExtension())
.$extends(disallowUndefinedDeleteUpdateManyExtension())
.$extends(withAccelerate());
export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions;